Skip to content

C语言拾遗

C语言拾遗

前言:实际上我觉得这些几乎都不重要,但是发现有些记忆迷糊了,权且记录一下.


- ASCLL码中: A 65 a 97

  • getchar() putchar() EOF = end of file = -1)

这两者可以这样搭配使用:

int c;//由于char类型并不知道是unsigned还是signed,因此用int
while((c=getchar())!=EOF)
{
    putchar(c);
}

  • %5d: 是右对齐,至少打印5位,如果不够5位就会在左边用空格补齐. -5d是左对齐,会在右边加上空格.
  • scanf的返回值是一个整数,表示成功读取的变量个数. 因此如果返回0,那么就意味着读取到0个变量;返回-1,则意味着发生错误或者读到结尾(这时-1就是EOF)
  • printf的返回值为打印的总字符个数.

  • goto语句和label必须处在同一个函数里.

  • 生成0-99随机数:
    srand(time(0));
    int num = rand()%100;
    

  • 引用另一个源文件的全局变量:extern int a;
  • static int a = 0;修饰局部变量会使得变量不会反复创建;修饰全局变量会使得其为内部链接.
  • VS2022调试:
    • 逐过程:F10
    • 逐语句:F11
    • 监视数字内容:arr,10
    • 二维数组:arr,2,3

  • 负数的补码和原码的转化都是非符号位取反,然后+1
  • a^a = 0,由此得出单身狗算法:一个数组中每个数字都出现两次,但有一个只出现一次.寻找这个数字只需要全部异或一遍
  • 交换的两种:
    a = a+b;//可能会越界
    b = a-b;
    a = a-b;
    
    a = a^b;
    b = a^b;
    a = a^b;//当a==b的时候,上述过程就坏了,且只能用于整数.速度也慢
    
  • 消去n最右边的1: n = n&(n-1) 或者写成n &= n-1 每次应用,可以得到:1101 -> 1100 ->1000

  • sizeof是操作符而不是函数的原因:可以写出 sizeof a
  • sizeof是编译期推断的,是通过类型推断的
    short s = 10;
    int i = 2;
    int n = sizeof(s = i+4)
    
    则这里由于int赋值给s后会截断,因此n是2
  • VS中的地址:
    • 0x00 00 00 0a,0a是低地址.
    • 0a(以一列显示) 00 00 00 这当中 0a是低地址
  • 指针类型决定了指针+1跳过多少字节,也决定了两个同类指针相减后得到的结果是什么
  • const:
    const int* p = &n//const修饰*p,表示不能修改*p,即p指向的内存空间的内容.
    int * const p = &n//const修饰p,表示不能修改p,也就是p的指向
    
  • #define NDEBUG就是自动忽视掉assert

  • 数组名就是首元素地址,除了两个例外:
  • sizeof 数字名
  • &arr 这时两者都是代表整个数组

    • 比如&arr + 1则会跳过整个数组
    • 注意,如果func(int arr[]) { sizeof arr//这里会得到4/8,因为数组已经退化 }
    • 数组指针:int (p)[10] = &arr; 这时候变量的类型就是int ()[10]
    • 函数指针: int add(int x,int y); int (pf)(int,int) = &add; 这样调用的时候pf就直接调用了函数,当然为了可读性,我们也可以写成 (pf)(2,3) 考题: (*(void(*)()) 0)();是什么? void(*)()是函数指针类型,外面的()是强制转化,也就是把地址0强制类型转化成函数指针,然后调用
    • typedef void(*pf)(int); 这样以后函数指针类型就是pf了

  • isspace判断空白字符

  • islower判断是否是小写字母.是,则返回非零数,否则,返回0
  • isupper isdigit tolower toupper
  • strcpy (str1,str2)把str2的内容拷贝到str1中
    • 拷贝是从str2的开头到str2的第一个\0
    • str1长度必须大于等于str2,比str2多的部分保持不变.但是注意,str2的\0也会被拷贝进来
  • strcat (str,p) 字符串追加
    • 删掉str末尾的\0,加上p的内容
    • 注意保证str的空间足够
    • 注意自己不能给自己追加.不过可以用strncat
  • strcmp (str1,str2) 按字典序比较
    • 如果str1>str2,返回值大于0
    • 如果str1==str2,返回值等于0
    • 如果str1<str2,返回值小于0
  • 以上三个函数,都有n版本.如果是strncpy,str2的前n个字符没有\0,那么也不会主动追加\0;如果str2长度小于n,那么会自动补齐\0.但是如果strncat,不论如何都会自动加\0
  • strstr(str1,str2)返回str2第一次出现在str1中的pos

  • erron :c语言中的一个全局变量.strerror:把erron转换成字符串.
    #include<errno.h>
    
    int main()
    {
    FILE* pf = fopen("add.txt","r");
    if(pf==null)
    {
        printf("fopen: %s\n",strerror(errno));
    //上面这句等价于: perror("fopen");
    }
    return 0;
    }
    

  • memcpy (des,src,n) n为字节数 最好不要写出拷贝的src和des在同一个数组这种事情.
  • memset (arr,c,n)把从arr开始的n字节变成c
  • memcmp(p1,p2,n)比较n个字节

    • 小问题:比较两个数组{0,4},{0,5},需要n为多少即可?
    • 为5,因为内存中:00 00 00 00 00 01 00 00 与 00 00 00 00 11 00 00 00即可
  • 不论大端还是小端,总会用4个字节中的较小地址来代表其地址.

  • 小端的想法比较符合地址空间:
    00  高地址
    00
    00
    01  低地址
    这里则是表示1,而不是01000000
    

  • 常见写法:
    typedef struct node
    {
        ...
    }node;
    
  • 结构体对齐规则
    • 对齐数 = min(默认对齐数,成员变量的大小)
    • 默认对齐数:VS中为8,Linux中则没有这个对齐数
    • 每个成员变量都在对齐数的整数倍的地址处
    • 结构体总大小为最大对齐数的整数倍
    • 数组不被视为一个成员.而是把数组中存储的类型(char/int)视为成员
  • 对齐的原因:减少访存次数,增加速度 因此我们需要让占用空间尽量小的成员尽量集中在一起

  • 位段:
    struct A
    {
        int _a :2;//表示a只用到2个位,如果赋值为4,那么会截断
        int _b :5;
        int _c:10;
        int _c:30;//注意不要超过32
    }
    
    跨平台性很差.在存储时,如果一个字节可以存储多个字段,如a和b,那么a和b就会存在这个字节当中.一个字节中还有一个位没用,这个位如何处理取决于编译器. 位段没有地址,不能取地址.它实际上创造了一个比char还小的存储单位.

  • 联合体: 大小即为最大成员的大小.如果最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍.

  • malloc: 是会返回null的.需要进行检查.
    int*p = malloc(...);
    if(p==null)
    {
    perror("malloc");
    return 1;
    }
    free (p);
    p = nullptr;
    
    这几句一定要有

  • calloc (num,size)创建num* size大小的空间,同时初始化
  • realloc(要调整的空间初始地址p,调整后的新内存大小);不会进行初始化
    • 最好用新的指针来接受relloc,因为一旦开辟失败,会导致原先的指针p失效,发生泄露
    • realloc(null,40)等于malloc(40) 以上两者都需要判空.

free同一块区域两次,会导致程序崩溃.free(null)不会出问题.


  • 柔性数组 C99
  • 一个数组,作为结构体的最后一个成员
  • 未知大小的数组
    struct A
    {
        int i;
        int a[];// int a[0];也可
    }
    
    sizeof A 不会计算柔性数组的大小.用法:malloc给其分配大小. struct A* p = (struct A*)malloc(sizeof(struct A) + 10*sizeof(int)) 扩容用realloc即可.

  • 文件操作
    FILE* pf = fopen("文件名","w/r/a");//write 会清空源文件
    //同时需要注意:文件名中的\可能需要转移
    
    
    fputc('x',pf);//向文件中写入c
    int ch = fgetc(pf);//从文件中读取一个字符
    //循环读取
    while((ch=fgetc(pf))!=EOF)
    {
        fputc(ch,stdout);
    }
    
    fputs("hello",pf);
    char arr[20] = {0};
    fgets(arr,20,pf);//第二个参数是最大读取字符数 这个函数返回null表示读取结束
    //注意,这个函数足够安全,如果最后读取的数据20字符里都没有\0,那么会把最后一个字符变成\0
    
    fprintf(pf,"hello");
    fscanf(pf,"%d",&a);
    
    //类似的:sprintf(s,"hhelo") 写入到s
    
    fwrite(arr,sizeof(int),7,pf)//从arr中提取7个元素,每个元素大小与int一样,写入到pf
    fread //相反
    
    fseek()//移动磁盘.不说了
    feof/ferror,判断读取结束的原因是否是遇见文件结尾/是否是因为错误而结束
    if(feof(fp))
        printf("...");
    else if(ferror(fp))
        printf("...");
    
    fflush(pf);//刷新缓冲区
    ......
    fclose(pf);
    pf = null;
    

  • 预处理:
    //常见两个预处理宏
    __LINE__,__FILE__ 常用
    //换行符
    #define DEBUG ("file: %s,\
            line: %s",\
            __LINE__,\
            __FILE__ )
    
    //正确定义 --参数独立,整体独立
    #define MAX(a,b) ( (a)>(b)?(a):(b))
    
    //条件编译:
    #if 
        //..
    #endif
    
    #if ..
    #elis..
    #else
    #endif
    
    #if defined(a)
    #ifdef a //等于上面这个
    #ifndef 
    

如果 %d 遇见空白符,那么会跳过.跳过的数据会被从缓冲区删掉.如果遇见字母,那么不会跳过也不会取用,而是直接从缓冲区跳出.


release时,会自动删掉assert


sizeof 一般而言是编译期求值的. 但是有个例外:c99的变长数组中,如果我们sizeof 数组名,那么这个sizeof不得不在运行时求值.