C语言学习笔记(四)

  1. 字符串常量属于静态存储类别,这说明如果在函数中使用字符串常量,该字符串只会被存储一次。
  2. 数组形式的字符串与指针形式的字符串的区别:通常,字符串都作为可执行文件的一部分存储在数据段中。当把程序载入内存时,也载入程序中的字符串。字符串存储在静态存储区。当程序开始运行,并为数组分配内存,这时候才将字符串拷贝到数组中。此时字符串有两个副本,一个是静态存储区的字符串常量,另一个是存储在数组里面的字符串。指针型的字符串也会使编译器为字符串在静态存储区预留空间,当指针被初始化时,会将字符串在静态存储区的首地址赋值给该指针。意思就是,字符数组做的是拷贝,而指针则指向静态存储区的字符串常量。字符串常量通常被视为const数据,所以指针也应当是const类型的。
  3. scanf()函数和%s只能读取一个单词,gets()函数读取整行输入,直达遇到换行符,然后丢掉换行符,存储其余字符,并在字符串尾部加入一个空字符。gets()函数无法检测数组是否可以装下输入,如果输入的字符过长,就会导致缓冲区溢出。
  4. 一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或者文件作用域。块是用一对花括号括起来的代码区域。定义在块中的变量具有块作用域。以前,具有块作用域的变量必须在块的开头声明,C99允许在块中的任意位置声明变量。
  5. 函数作用域仅用于goto语句的标签,这意味着即使一个标签首次出现在函数的内层块中,它的作用域也延伸至整个函数。函数原型作用域用于函数原型中的形参名。函数原型作用域的范围从形参定义处到原型声明结束,这意味着,编译器在处理函数原型中的形参时,只关心它的类型,而形参名通常无关紧要,只有在变长数组中,形参名才有用:void use_a_VLA(int n,int m,int ar[n][m]);
  6. 变量的定义在函数的外部,具有文件作用域。具有文件作用域的变量,从它的定义处到该定义所在文件的末尾均可见。文件作用域变量也称全局变量。
  7. C编译器在预处理阶段,会将所有的#include指令用对应的头文件内容所替换。因此,编译器会将源代码文件和所有的头文件都看成是一个包含信息的单独文件,这个文件称为翻译单元。一个具有文件作用域的变量,它的实际可见范围是整个翻译单元。如果程序由多个源代码文件组成,那么该程序也将由多个翻译单元组成,每个翻译单元对应一个源代码文件和它所包含的文件。
  8. C变量有3种链接属性:外部链接、内部链接、无链接。具有块作用域、函数作用域或函数原型作用域的变量都是无链接变量。具有文件作用域的变量可以是外部链接或者内部链接。外部链接可以在多文件程序中使用,内部链接变量只能在一个翻译单元里面使用。使用static关键字修饰的全局变量为内部链接变量,未使用static修饰的全局变量为外部链接变量。
  9. 作用域和链接描述了标识符的可见性。存储期描述了通过标识符访问的对象的生存期。C对象有4种存储期:静态存储期,线程存储期,自动存储期,动态分配存储期。
  10. 文件作用域变量具有静态存储期,它在程序执行的期间一直存在。需要注意的是,对于文件作用域变量,关键字static表明了其链接属性,而非存储期。
  11. 线程存储期用于并发程序设计,具有线程存储期的对象,从被声明时到线程结束一直存在。以关键字_Thread_local声明一个对象时,每个线程获得该变量的一个私有备份。
  12. 块作用域的变量通常具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存,当退出这个块时,释放为变量分配的内存。但是变长数组稍有不同,它们的存储期从声明处到块的结尾,而不是从块的开始处到块的结尾。需要注意的是,块作用域变量也可以有静态存储期。只需用static修饰即可。
  13. 关键字auto是存储类别说明符,auto关键字在C++中的用法完全不同,如果编写C/C++兼容的程序,最好不要使用auto作为存储类别说明符。
  14. 自动变量不会初始化,除非显式初始化它。
  15. 寄存器变量存储在寄存器而非内存中,所以是无法获取寄存器变量的地址的。绝大多数方面,寄存器变量和自动变量一样,块作用域、无链接和自动存储期。编译器必须根据寄存器或最快可用内存的数量衡量你的请求,或者忽略你的请求,此时,寄存器变量就变成了普通的自动变量,但是,仍然不能对该变量使用地址运算符。可声明为register的数据类型有限,例如,处理器中的寄存器可能没有足够大的空间来存储double类型的值。
  16. 外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别。该类别的变量称为外部变量。在执行块中的语句时,块作用域中的变量将“隐藏”文件作用域中的同名变量,如果不得已要使用与外部变量同名的局部变量,可以再局部变量的声明中使用auto存储类别说明符明确表达这种意图。
  17. 外部变量和自动变量类似,可以被显式初始化。外部变量未被显式初始化时,他们会被自动初始化为0.需要说明的是,只能使用常量表达式初始化文件作用域变量。
  18. C99和C11标准都要求编译器识别局部标识符的前63个字符和外部标识符的前31个字符。在此之前,编译器识别局部标识符的前31个字符和外部标识符的前6个字符。
  19. 定义式声明和引用式声明。
  20. 外部变量只能初始化一次,且必须在定义该变量时进行。
  21. 函数具有存储类别,可以是外部函数或者是静态函数。C99增加了内联函数。外部函数可以被其他文件的函数访问,但是静态函数只能用于其定义所在的文件。以static存储类别说明符创建的函数属于特定模块私有,这样做避免了命名冲突的问题。用extern关键字声明定义在其他文件中的函数,这样做,是为了表明当前文件中使用的函数被定义在别处。除非使用static关键字,否则一般函数声明都默认为extern。
  22. 因为char类型为1个字节大小,所以malloc()的返回类型通常被定义为指向char的指针。但是从ANSI C开始,C使用一个新的类型:指向void的指针。该类型相当于一个“通用指针”。
  23. 分配内存还可以用calloc(),和malloc类似,在ANSI之前,返回char的指针,之后,改为void的指针。calloc接收两个无符号整型参数,第一个表示所需存储单元的数量,第二个表示存储单元的大小。calloc的另一个特性在于:它把块中的所有位都设置为0。free()函数也可以用于释放calloc分配的内存。
  24. C90新增了两个属性:恒常性和易变性。分别用关键字const和volatile来声明。以这两个关键字创建的类型是限定类型。C99新增了第三个限定符restrict,用于提高编译器优化。C11新增了第四个限定符:_Atomic,用于支持并发程序设计。C99为类型限定符增加了一个新的属性,idempotent。它的意思是可以在一条声明中多次使用同一个限定符,多余的限定符会被省略。如const const const int a = 9;
  25. C99允许把类型限定符和存储类别说明符static放在函数原型和函数头的形式参数的初始方括号内。对于类型限定符,这是为了给现有功能提供一个替代语法。就是语法如:void func(int * const a1,int * restrict a2,int n);新的等价语法则是:void func(int a1[const],int a2[restrict],int n);对于static,新标准为static引入了一种与以前语法不相关的新用法。static新的用法告知编译器如何使用形式参数。如下:double stick(double ar[static 20]);这种用法表明,函数调用中的实际参数是一个指向数组首元素的指针,且该数组具有至少20个元素。
  26. 文件通常是磁盘或者固态硬盘上的一段已命名的存储区。C把文件看作是一系列连续的字节,每个字节都能被单独的读取。C提供两种文件模式:文本模式和二进制模式。
  27. 所有的文件都是以二进制形式存储的,但是,如果文件最初使用二进制编码的字符来表示文本,该文件就是文本文件。如果文件中的二进制代表机器语言代码或者数值数据,该文件就是二进制文件。
  28. UNIX用同一种文件格式处理文本文件和二进制文件的内容。C和Unix在文本中都使用\n表示换行。其他的系统则使用一种与Unix模型不同的格式处理文本文件。
  29. C语言在二进制模式下,程序可以访问文件的每一个字节。而在文本模式下,程序所见的内容和文件的实际内容不同,程序以文本模式读取文件内容时,把本地环境表示的行末尾或文件结尾映射为C模式。比如将文件中的\r\n转换为\n,在写入文件时,又将\n转换为\r\n。
  30. I/O的两个级别:底层I/O使用操作系统提供的基本I/O服务;标准高级I/O使用C库的标准包和stdio.h头文件定义。因无法保证所有操作系统使用相同的底层I/O模型,C标准只支持标准I/O包。
  31. C程序会自动打开3个文件,标准输入、标准输出、标准错误输出。
  32. 与底层I/O相比,标准I/O包除了可移植以外,还有两个好处:第一,标准I/O有许多专门的函数简化了处理不同I/O的问题。第二,输入和输出都是缓冲的,也就是说,一次转移一大块信息而不是一字节信息。缓冲机制,提高了数据传输的效率。
  33. exit()函数关闭所有打开的文件并结束程序。exit()的参数被传递给一些操作系统,通常的惯例是:正常结束的程序传递0,异常结束的程序传递非零。不同的退出值可用于区分程序失败的不同原因。根据ANSI C的规定,在最初调用的main()中使用return与调用exit()的效果相同。但要注意的是,只针对“在最初调用的main()中”。入股main()在一个递归程序中,exit()仍会终止程序,而return只会把控制权交给上一级递归,直至最初的一级。return和exit的另一个区别在于,即使在其他函数中调用exit(),也会导致整个程序的终止。
  34. ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200308232513646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzI4NzA1Nzcz,size_16,color_FFFFFF,t_70)
    
  35. 对于UNIX和LINUX这样只有一种文件类型的系统,带b字母的模块和不带b字母的模式相同。C11新增了带x字母的写模式,与以前的写模式相比具有更多的特性:第一,使用带x字母的写模式,即使fopen()操作失败,原文件的内容不会被删除。第二,如果环境允许,x模式的独占特性会使得其他程序或线程无法访问正在被打开的文件。
  36. C99和C11为结构提供了指定初始化器,其语法与数组的指定初始化器类似。但是,结构的指定初始化器使用点运算符和成员名标识特定的元素。
  37. C99增加了一个特性:伸缩型数组成员。这样声明的结构体,它的最后一个数组成员具有以下特性:该数组不会立马存在。使用这个伸缩型数组成员可以编写合适的代码,就好像它确实存在并具有所需数目的元素一样。
  38. 伸缩数组成员的规则:伸缩型数组成员必须是结构的最后一个成员;结构中必须至少含有一个成员;伸缩数组的声明类似于普通数组,只是它的方括号中是空的。声明一个这种结构类型的变量时,不能用这个伸缩数组做任何事,因为并没有为这个数组预留存储空间。C99的意图并非使我们声明一个变量,而是一个该结构类型的指针,利用malloc分配动态内存,内存大小应该为结构体的大小外加数组的大小。比如:malloc(sizeof(struct Flex) + 5 * sizeof(double));此时额外分配了5个double类型的空间,作为伸缩数组的空间。
  39. 需要注意的是,带伸缩型数组成员的结构,有一些特殊的处理要求,第一,不能用结构进行赋值或者拷贝。比如struct flex *pf1,*pf2;*pf1 = *pf2;这样做只能拷贝除伸缩型数组成员以外的其他成员。确实需要进行拷贝时,需使用memcpy()函数。第二,不要以按值方式把这种结构传递给结构。原因相同,按值传递一个参数与赋值类似。要把结构的地址传递给函数。第三,不要使用带伸缩型数组成员的结构作为数组成员或另一个结构的成员。
  40. 类似于在结构中最后一个成员是伸缩型数组的情况,这种称为struct hack。除了伸缩型数组成员在声明时用空的方括号以外,struct hack特指大小为0的数组。然而,struct hack是针对特殊编译器(GCC)的,不属于C标准。
  41. 匿名结构是一个没有名称的结构成员。在C11中,可以用嵌套的匿名成员结构定义一个嵌套的结构体。访问结构体中的匿名结构体的成员时,只需把它们当作结构体的成员来处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值