Skip to content

Latest commit

 

History

History
452 lines (388 loc) · 10.6 KB

基础语法.md

File metadata and controls

452 lines (388 loc) · 10.6 KB

基础语法

程序结构

示例
官网

参数

示例

int main(int argc, char * argv[]) {
    printf("参数个数: %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }
};

注释

链接

// 单行
/* 多行
*/
  • size_t 一般是8字节的无符号整数
  • char 1字节
  • unsigned char 1字节
  • range 一组数据
{0, 1, 2, 3}

整数

int a = 052;  // 八进制, 所以是42
int a = 0x12;  // 十六进制;
  • int 4字节(根据系统而定)
  • long 8字节(根据系统而定)

小数

  • float 只能保留7位小数(根据系统而定)
  • double float
  • long double

变量

链接
局部变量的初始值是不确定的. 但是全局变量的初始值默认是0(估计是因为全局变量的值写入了elf)

int a;  a 的值不确定哦  // 10亿次需要2.076秒,快了大概3%
int a = 0; a 确定是0  // 10亿次需要2.143秒 
int a, b, c;
a = b = c = 4;  // 可以同时赋值
int var; 声明并初始化var, 申请了变量的内存地址
extern int var; 仅仅声明, 不需要编译器申请内存
  • 寄存器变量
register int a;  // a保存在寄存器里, 不保证, 只是个hint
  • static变量
static int a;  // 变量只能被当前文件访问
另外一个好处是, 函数内的static, 运行完毕后不会被销毁. 所以全局静态变量可以放在函数内部. 并且这个变量只会被初始化一次
int increment() {
    static int a = 1; 必须赋予一个constant value, 不能等于一个变量
    a++;
    return a;
};
  • const常量
#define NAME value  // 但是这样会导致所有用的地方都赋值一份到内存吧
const value

指针

定义指针

struct Student *p;  // 定义结构体的指针
p = &student
p + 1  // 这样会直接增加strudent的size
  • 指针的优先级 []优先级比*
int *ptr[10]; ptr是个数组(长度为10), 里面每个元素是int的指针
int (*ptr)[10]; ptr是一个指针(数组第0个元素), 每个元素都是int
  • 函数的指针
int add(int a, int )
{
    return a + b;
}
int (*f)(int, int) = &add;  // 实际上有没有&都可以, 因为编译器知道函数名是没有值的, 只有地址
*f(1, 2)  // 实际上*有没有都可以, 因为f本身指针不支持执行函数, 所以没有语义歧义

使用指针

p->age;  // 获取p对应的student的age

内置函数

  • sizeof(int/a/a[0]) unary operator, 不是函数哦, 里面的值不会被计算(除非是数组变量)
    返回占用内存字节大小
int i = 1;
printf("%d\n", sizeof(i++));  // i不会被+1哦
printf("%lu", sizeof(1));
>>> 4

字符串

  • 赋值
#include <string.h>
strcpy( Student.name, "ramwin");

函数

函数默认返回的是int. 所以哪怕不声明(declaration)函数, 也能编译成功. 但如果函数最后返回char, 就会报错

  • 定义 definition 完整的实现的代码

数组

  • 访问
char a[] = {'a', 'b', 'c'}
a[1], *(a+1), 1[a], 都是b哦
  • 字符数组
char a[6];
char a[6] = {"s", "t", "r", "i", "n", "g}  // 有人说最后要加\0, 但是我字符串都固定长度了,最后不需要加\0了吧。
print("%s", a)  // 只会输出 string,试了好多次,总觉得是gcc在最后默认加\0了, 测试验证,的确是的 a[6] 总是0, a[7]是随机。那不就内存不安全了吗,经过测试,应该是超过程序自带的内存就报错,否则是返回真正的内存的?
print("%lu\n", a[50])  // 总是不报错并且返回一个数字
print("%lu\n", a[5000])  // 有可能因为内存不够超过范围而报错
  • 初始化
int arr[100];  // 不初始化, 不保证是0
int arr[] = {[0]=1, [5]=2};  // 默认长度就是6
int arr[] = {1, 2, 3}  // 不设置长度, 就是3
int arr[] = {1, 7, 5 [5]=90, 6, [8] = 5};  // [5]=90, 那么6就是这是在[6]上的
int arr[] = {1, 2, 3, [2] =4, [6]=45};  // 冲突时, 行为未定义, 不要这么写
int arr[y] = {...};  // error, 边长数组不能初始化

二维数组

int a[3][2] = {  // 注意前面是行数, 后面是列数
    {1, 2},
    {3, 4},
    {5, 6},
}

结构体

例子

struct {  // 可以不设置struct的name, 直接使用
    char *engine;
    int fuel_tank_cap;
} car1, car2;

struct Person {
    char name[30];
    int age;
    int id;
};

int main() {
    struct Person person = {"张三", 18, 1};
    struct Person person2 = {
        .name = "张三", .id=3, .age=18}
    printf(person.name);
    return 0;
};
  • 定义
#pragma pack(1)  // 使用pragma pack(1)使得结构体不四字节对其
struct abc {
    char a;
    int b;
    char c;
}

初始化

struct Person person = {"张三", 18, 1};  // 可以按顺序设置
struct Person person2 = {  // 可以按照属性设置
    .name = "张三", .id=3, .age=18}
  • 结构体必须手动初始化, 只有全局变量才会自动初始化
#include <stdio.h>

struct Student {
    char a;
    int b[1000000];
    char c;
};

void f() {
    struct Student c;
    printf("字符是: %c end", c.c);
    printf("字符是: %d end", (int)c.c);
    // 字符是:  end字符是: 0 end字符是: a end字符是: 97 end 栈内存不会自动清零
    c.c = 'a';
};

int main() {
    f();
    f();
};
struct Person a;
struct Animal *f = (struct Animal *) &a;
  • 如果强制访问超内存的结构体 测试
    这样会导致结构体访问的内存不再是属于程序, 如果内存没有超过, 会导致误修改其他变量
int a = 0;
int b = 18;
struct Person *person;
person = &a;
person->age = 32;  // b会变成32

Union

  • union的类型可以有名字或者没有名字. 如果没有名字, 就要保证元素名都不相同
#include <stdio.h>

struct store {
    union {
        struct {
            int time;
            int price;
        } book;
        struct {
            int price;
            int time;
        } movie;
        struct {  // 这个结构体没有名字, 直接通过ab访问. 只要不出现 book 或者 movie 就行
            int a;
            int b;
            int price;  // 可以和movie内部的元素重名
        };
    } item;
} s;

int main() {
    s.item.book.price = 1998;
    printf("%d\n", s.item.movie.time);
    printf("%d\n", s.item.b);
};

Atomic Types

示例

atomic_int a
a ++; 这样的a可以被多线程进行操作. 同时拿到的值也是不一定的

输入

  • scanf
int var, varb;
scanf("%d%d", &var, &varb);  # 遇到space才输入, 所以可以输入"123 456\n" "123\n456\n"
char a[10];
scanf("%9s", a);  可以指定输入9个字符
  • gets 获取整行输入, 这个已经弃用了. 用fgets
char a[10];
gets(a);
  • fgets 注意, 后面的size参数包括了null, 所以只能接受3个字符. 并且会把回车的\n也输入进去
char a[4];
fgets(a, 4, stdin);  // ab\n
a = {'a', 'b', '\n', '\0'}

输出

puts

puts输出字符串时,会自动添加换行符

char *s = "hello";
puts(s);

printf格式化

输出并会返回输出内容的长度

printf("%lu", <32位无符号整数>)  // 但是lu也能显示超过32位的,怀疑有兼容
printf("%llu", <64位无符号整数>)
printf("%s", <字符串>)
  • %u: 无符号整数(short或者int)

  • %d: 整数, short int

  • %p: 地址

  • %c: 字符

  • %f: 浮点数, %.2f 必定保留2位小数

  • %s: 字符串, %10s 必定保留至少10的宽度(前面加空格) %6.5s 6代表输出占用6个字符宽度, 5代表只读取字符串前5个字符

  • %o: 八进制数字

  • %x/%X: 十六进制数字(会根据x或者X变化大小写)

Operator

  • lvalue: 存在identifiable location in memory的object 所以必须是变量. 不能是expression, function, const
  • rvalue: 不存在identifiable location 表达式, 函数, 常量
  • comma operator 计算所有的操作, 但是只返回最后一个 int a = (3, 4, 8), 所以a=8

binary operator

需要2个operand的操作 +, -, *, /, %, &&, ||, ==, !=, <=, >=, <, >

  • logical operators 当前面的计算已经得到结果时, 后续的判断就不会再运算
    • &&, || & bitwise and
    • &, |, ^

unary operator

++, --, 必须作用于lvalue
++a, pre-incrementing, 先加后用
a++, post-incrementing
~, not, 所有bit取反

Precedence

  • assignment, =, +=, *=
  • comma , 顺序最低

Associativity

Associativity只有在同时存在2个operator才有效果

a = func1() + func2();  因为这里只有一个operator, 所以不会进行associativity的判定. 所以不能保证func1一定先执行

判断

switch

  • 用法
switch(x) {  // x或者x的结果必须是整数. 因此case后面的值也必须是整数
    case 1:  // case后面不能是变量
        xxx;
        break;
    case 2:  // case不允许重复
        xxx;
        break;
    default:  // default可以放在任何位置
        xxx;
        break;
}
  • 原理
switch (a) {  // 先通过a和各个数字比较, 判断jump到哪个命令
1138:       83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
113c:       74 08                   je     1146 <main+0x1d>
113e:       83 7d fc 02             cmpl   $0x2,-0x4(%rbp)
1142:       74 08                   je     114c <main+0x23>
1144:       eb 0c                   jmp    1152 <main+0x29>
/home/wangx/test.c:8
    case 1:
        a+=1;
1146:       83 45 fc 01             addl   $0x1,-0x4(%rbp)
/home/wangx/test.c:9
        break;  // 没有Break的话,就无法跳出了
114a:       eb 0b                   jmp    1157 <main+0x2e>
/home/wangx/test.c:11
    case 2:
        a+=2;
114c:       83 45 fc 02             addl   $0x2,-0x4(%rbp)
/home/wangx/test.c:12
        break;
1150:       eb 05                   jmp    1157 <main+0x2e>
/home/wangx/test.c:14
    default:
        a+=3;
1152:       83 45 fc 03             addl   $0x3,-0x4(%rbp)
/home/wangx/test.c:15
        break;

循环

  • while
while(expression)
{
    Statements1;
    Statements2;
}
  • for
  • do while loop 当你的循环至少需要执行一次的时候,请用do while loop
do {
    Statements1;
    Statements2;
} while (expression);