the C programming language 阅读笔记1

1. 指针

读了一遍著名的《the C programming
language》,果然如听说的一样,讲解基础透彻,案例简单典型,确实自己C语言还有很多细节点不是很清楚。

读了一遍著名的《the C programming
language》,果然如听说的一样,讲解基础透彻,案例简单典型,确实自己C语言还有很多细节点不是很清楚。

GCC’s Doc–gcc-5-doc is a very ugly written work by the organization.

The gcc-5-doc is very difficult to read, and impossible for a beginner
to compile the first work. The doc in html format which is over three
Megabits (no graphics) is just a stack of nonsense rubbish.

Even if you read through the doc thoroughly, you can not compile the
first work, ’cause it does not introduce to us this procedure. There is
not any demo to show how to compile a source code into an executable
one. What there exist are a bunch of options that we should care about
when compiling the source code.

It is just like an instruction for an SEM telling us how to get a
possible clear image, but it forgets to tell us how to start it, and it
has forgotten for years. Funny?


Here is a useful link:
gcc编译C语言程序的过程

威尼斯网址开户网站 1

C.png

It should be pointed out that this is not the one and only way to
compile a C work. Let’s see the following ways:

威尼斯网址开户网站 2

GCC.png

As it shown above, the simplest is gcc demo.c -o demo.xyz. And you may
have noticed that the extension of the file is not important.

Below is a list of options passed to gcc.

mark@ASUS:~/Desktop$ gcc --help
Usage: gcc [options] file...
Options:
  -pass-exit-codes         Exit with highest error code from a phase
  --help                   Display this information
  --target-help            Display target specific command line options
  --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
                           Display specific types of command line options
  (Use '-v --help' to display command line options of sub-processes)
  --version                Display compiler version information
  -dumpspecs               Display all of the built in spec strings
  -dumpversion             Display the version of the compiler
  -dumpmachine             Display the compiler's target processor
  -print-search-dirs       Display the directories in the compiler's search path
  -print-libgcc-file-name  Display the name of the compiler's companion library
  -print-file-name=<lib>   Display the full path to library <lib>
  -print-prog-name=<prog>  Display the full path to compiler component <prog>
  -print-multiarch         Display the target's normalized GNU triplet, used as
                           a component in the library path
  -print-multi-directory   Display the root directory for versions of libgcc
  -print-multi-lib         Display the mapping between command line options and
                           multiple library search directories
  -print-multi-os-directory Display the relative path to OS libraries
  -print-sysroot           Display the target libraries directory
  -print-sysroot-headers-suffix Display the sysroot suffix used to find headers
  -Wa,<options>            Pass comma-separated <options> on to the assembler
  -Wp,<options>            Pass comma-separated <options> on to the preprocessor
  -Wl,<options>            Pass comma-separated <options> on to the linker
  -Xassembler <arg>        Pass <arg> on to the assembler
  -Xpreprocessor <arg>     Pass <arg> on to the preprocessor
  -Xlinker <arg>           Pass <arg> on to the linker
  -save-temps              Do not delete intermediate files
  -save-temps=<arg>        Do not delete intermediate files
  -no-canonical-prefixes   Do not canonicalize paths when building relative
                           prefixes to other gcc components
  -pipe                    Use pipes rather than intermediate files
  -time                    Time the execution of each subprocess
  -specs=<file>            Override built-in specs with the contents of <file>
  -std=<standard>          Assume that the input sources are for <standard>
  --sysroot=<directory>    Use <directory> as the root directory for headers
                           and libraries
  -B <directory>           Add <directory> to the compiler's search paths
  -v                       Display the programs invoked by the compiler
  -###                     Like -v but options quoted and commands not executed
  -E                       Preprocess only; do not compile, assemble or link
  -S                       Compile only; do not assemble or link
  -c                       Compile and assemble, but do not link
  -o <file>                Place the output into <file>
  -pie                     Create a position independent executable
  -shared                  Create a shared library
  -x <language>            Specify the language of the following input files
                           Permissible languages include: c c++ assembler none
                           'none' means revert to the default behavior of
                           guessing the language based on the file's extension

Options starting with -g, -f, -m, -O, -W, or --param are automatically
 passed on to the various sub-processes invoked by gcc.  In order to pass
 other options on to these processes the -W<letter> options must be used.

For bug reporting instructions, please see:
<file:///usr/share/doc/gcc-5/README.Bugs>.
mark@ASUS:~/Desktop$ 

1.1 自增符的使用

总结一下阅读的收获(部分原书不清晰的知识点在网络上搜索后补充,引用出处忘记了,原作者看到可联系添加)

总结一下阅读的收获(部分原书不清晰的知识点在网络上搜索后补充,引用出处忘记了,原作者看到可联系添加)

  ++*p;//p指向的内容加一

1.声明

1.声明

  (*p)++; //p指向的内容加一

1.1 变量声明

1.1 变量声明

  *p++;//p本身自增

  在C语言中,所有变量都必须先说明后使用,说明通常放在函数开始处的可执行语句之前。

  在C语言中,所有变量都必须先说明后使用,说明通常放在函数开始处的可执行语句之前。

  *++p; //p本身自增

1.2 外部变量

1.2 外部变量

  因为诸如*和++这样的一元运算符在表达式求值时按从右到左的顺序和运算分量结合。

  在每一个函数中都要对所要访问的外部变量进行声明。声明所使用的外部变量的类型,在声明时可以用extern显式说明,也可以通过上下文隐式说明。如果外部变量的定义在源文件中出现在使用它的函数之前,则extern声明可以省略。

  在每一个函数中都要对所要访问的外部变量进行声明。声明所使用的外部变量的类型,在声明时可以用extern显式说明,也可以通过上下文隐式说明。如果外部变量的定义在源文件中出现在使用它的函数之前,则extern声明可以省略。

1.2 指针运算比数组下标运算快

  如果程序包含几个源文件,某个变量在file1中定义,在file2与file3中使用,那么file2和file3文件中就需要extern声明来连接该变量的出现。

  如果程序包含几个源文件,某个变量在file1中定义,在file2与file3中使用,那么file2和file3文件中就需要extern声明来连接该变量的出现。

1.3 数组名

  变量extern声明和函数声明放在头文件中。

  变量extern声明和函数声明放在头文件中。

  一个数组名即该数组第0个元素的位置,所以赋值语句pa = &a[0]等价于pa
= a

1.3 声明的风格

1.3 声明的风格

1.4  数组下标求值

  声明的旧风格:print();(省去的返回值int,简化编码)

  声明的旧风格:print();(省去的返回值int,简化编码)

  在求数组元素a[i]的值时,C语言实际上先将其转换成*(a +
i)的形式再求值

  声明的新风格:void print(void)

  声明的新风格:void print

  而对于指针pa而言,pa[i] 等价于 *(pa + i)

  新风格的优点:编译器帮助更好地检查函数调用

  新风格的优点:编译器帮助更好地检查函数调用

1.5 数组名与指向数组首地址的指针的区别

  如:

威尼斯网址开户网站,  如:

  前者非变量,后者为变量

  print(5.5);

  print;

1.6 函数参数传入指针

  在旧风格下,该次调用编译器不会报错(因为参数列表没有内容意味着不对参数列表进行检查)

  在旧风格下,该次调用编译器不会报错(因为参数列表没有内容意味着不对参数列表进行检查)

  在函数的定义中,char s[]与char *s是等价的。

  在新风格下,编译器会报错

  在新风格下,编译器会报错

  当函数参数传入一个较大数组的子数组的指针时,并不会对函数本身造成影响

1.4 声明的重要性(编译器检查之反例)

1.4声明的重要性

1.7 指针使用负下标

  在同一源文件中,函数的声明必须与其定义一致,否则编译错误。但如果函数是独立编译的,则这种不匹配就不会检测出来。因为C语言会对没有函数声明的函数自动进行 隐式声明,那么编译器不仅不会对返回值进行检测,更不会对参数列表进行检测。

  在同一源文件中,函数的声明必须与其定义一致,否则编译错误。但如果函数是独立编译的,则这种不匹配就不会检测出来。因为C语言会对没有函数声明的函数自动进行 隐式声明,那么编译器不仅不会对返回值进行检测,更不会对参数列表进行检测。

  如果确信某个元素存在,则使用p[ -1]、p[ -2
]这样的表达式在语法上是合法的。

  main.c 

  main.c 

 b[] = {,,,,,,,,, *a = b + ;
       printf(“%f”,getNum( }
1 int main()2 {3     printf(“%f”,getNum(123));4 }

  则:a[-3] = 1; a[ -2] = 2;

  get_num.c

  get_num.c

1.8 alloc与afree的栈式管理

  get_num(        }
1 double get_num(void)2 {3     return 3.14;4 }

  先分配的后释放

  编译不会有任何错误,务必记得使用函数声明

  编译不会有任何错误,务必记得使用函数声明

  调用alloc之前:

  在缺省的情况下,外部变量与函数具有如下性质:所有通过名字对外部变量与函数的引用(即使这种引用来自独立编译的函数)都是引用的同一对象。

  在缺省的情况下,外部变量与函数具有如下性质:所有通过名字对外部变量与函数的引用(即使这种引用来自独立编译的函数)都是引用的同一对象。

  allocbuf:

2. 内存布局

2.内存布局

威尼斯网址开户网站 3

2.1 int的字节长度

2.1int的字节长度

  调用alloc之后:

  在C语言中int的长度

  在C语言中int的长度

  allocbuf:

  1.      long int型至少应该与int型一样长,而int型至少与short
int型一样长;

  1. long int型至少应该与int型一样长,而int型至少与short int型一样长;

威尼斯网址开户网站 4

  2.      C/C++规定int的字长与机器字长相同

  2. C/C++规定int的字长与机器字长相同

  afree(p)是将空闲单元起始指针指向p位置

  3.      操作系统字长与机器字长未必一致

  3. 操作系统字长与机器字长未必一致

1.9 比较运算

  4.      编译器根据操作系统字长来定义int字长

  4. 编译器根据操作系统字长来定义int字长

  在知道指针值意义的情况下,可以对其进行比较运算

  由上面4点可知,在一些没有操作系统的嵌入式计算机系统上,int的字长与处理器字长相同;有操作系统时,操作系统字长与处理器字长不一定一致,此时编译器根据操作系统的字长来定义int字长。比如你在64位机器上运行DOS的16位系统,那么所有for
dos的C/C++编译器中的int都是16位的;在64位机器上运行win32系统,那么所有for
win32的C/C++编译器中的int都是32位的。

  由上面4点可知,在一些没有操作系统的嵌入式计算机系统上,int的字长与处理器字长相同;有操作系统时,操作系统字长与处理器字长不一定一致,此时编译器根据操作系统的字长来定义int字长。比如你在64位机器上运行DOS的16位系统,那么所有for
dos的C/C++编译器中的int都是16位的;在64位机器上运行win32系统,那么所有for
win32的C/C++编译器中的int都是32位的。

   b[] = {,,,,,,,,, *p1 = &b[ *p2 = &b[];

  原因:

  原因:

  则p2 – p1 = 4

  操作系统决定了软件层面对于硬件的管理方式,那么对于64位的机器,如果管理方式仍然是16位的(如内存访问地址仅为216),那么该64位机器其实也只发挥了16位的作用。

  操作系统决定了软件层面对于硬件的管理方式,那么对于64位的机器,如果管理方式仍然是16位的(如内存访问地址仅为216),那么该64位机器其实也只发挥了16位的作用。

1.10 指针相关的有效运算

  对于不同的平台,有其相应的指令集,也就有其对应的编译器。C语言在源代码层面具有可移植性就是这个原因,只要在不同的平台,使用不同的编译器,即使最终得到的二进制机器码不同,程序运行结果也一定是相同的。

  对于不同的平台,有其相应的指令集,也就有其对应的编译器。C语言在源代码层面具有可移植性就是这个原因,只要在不同的平台,使用不同的编译器,即使最终得到的二进制机器码不同,程序运行结果也一定是相同的。

1.11 strcpy函数

  因此,定义数据结构时(尤其是嵌入式)时,优秀的程序员不会如下定义(32位):

  因此,定义数据结构时时,优秀的程序员不会如下定义:

  原书上的函数:

 typedef  
 unsigned  
 unsigned  
 }TypeExample;
1 typedef struct tagTypeExample{2 3 unsigned short x;4 5 unsigned int y;6 7 }TypeExample;
 strcpy(  *s,  *( *s++ = *t++

他们这样定义:

他们这样定义:

  有错误,s的指针变量,虽然内容被复制的,但是找不到该段内容的首地址了,应该为:

  unsigned short UINT16

  unsigned int UINT32

 typedef  


 }TypeExample;
 1 #define unsigned short UINT16 2  3 #define unsigned int UINT32 4  5 typedef struct tagTypeExample{ 6  7 UINT x; 8  9 UINT y;10 11 }TypeExample;
 *strcpy(  *s,  *

     *( *cp++ == *t++

这样,换平台的话,只需要改变宏定义即可

1.12 函数参数为二维数组

  根据IEEE754的标准,float和double的内存布局如下:

2.2float和double的范围和有效数字

  f( int daytab[2][13])

  符号位S(1 bit) + 指数(8 bits) + 尾数(23 bits)

  根据IEEE754的标准,float和double的内存布局如下:

  = f(int daytab[][13])

  float长度32位

  符号位S + 指数 + 尾数

  =f(int (*daytab)[13])

  计算方式:

  float长度32位

  传入一个指针,指针每个元素的内容是一个数组

  指数上为移码,偏移数为127

  计算方式:

1.13指向数组的指针和指针数组

  尾数上省去了最高位的1,仅作为小数点后的二进制表示

  指数上为移码,偏移数为127

  int (*daytab)[13] –>指针指向的内容是int[13]的数组

  (-1)^S(1+尾数)*2^(指数-偏移数)

  尾数上省去了最高位的1,仅作为小数点后的二进制表示

  int *daytab[13]à13个指针指向int

  例:3.0 = 11 = (-1)^0 * (1.1) * 2^(128 -127)

  ^S*2^

  指针的实质是根据定义的类型和其中存储的地址来解析内存里的数据

  故:符号位为0,指数为1298,尾数为:1+22个0

  例:3.0 = 11 = ^0 * * 2^

  print (Int *s){

  所以:3.0的float为:0 10000000 10000000000000000000000 = 0x40400000

  故:符号位为0,指数为1298,尾数为:1+22个0

    s[1] = 5;//即为*(s+1) = 5;

  验证:

  所以:3.0的float为:0 10000000 10000000000000000000000 = 0x40400000

  };

 #include <stdio.h>

                    t.b =      printf( }

  验证:

   *(s+1) = 5;

  结果输出为:3.000000

 1 #include <stdio.h> 2  3 union test{ 4     float a; 5     int b; 6 }; 7 int main() 8 { 9     union test t;10     t.b = 0x40400000;11     printf("%f\n",t.a);12 }

  根据指针所存的地址及所定义的类型,改变其中的数据

  因此,指数127~128,范围为:2^-127~2^128

  结果输出为:3.000000

   int (*daytab)[13]

  尾数:隐藏的1永远不变,不会影响精度。2^23 =
8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字. 

  因此,指数-127~128,范围为:2^-127~2^128

  所定义的类型为int[13]的数组

  同样,对于内存布局为:

  尾数:隐藏的1永远不变,不会影响精度。2^23 =
8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字.

  所以,对该指针解引用得到的是一段13个int长的内存区域的首地址

  符号位S(1 bit) + 指数(11 bits) + 尾数(52 bits)

  同样,对于内存布局为:

  即:

  的double类型来说,分析是相同的。

  符号位S + 指数 + 尾数

  int a[4][4];  int (*b)[4] = a;

  范围:-2^1023 ~ +2^1024

  的double类型来说,分析是相同的。

  威尼斯网址开户网站 5

  有效数字:2^52 =
4503599627370496,一共16位,同理,double的精度为15~16位。

  范围:-2^1023 ~ +2^1024

  b[2][2] = *(*(b+2)+2) = (*(s+2))[2]  != *(s+2)[2]

 

  有效数字:2^52 =
4503599627370496,一共16位,同理,double的精度为15~16位。

  分析:

3. 类型

3. 类型

  *b解引用得到:int[4]

3.1 char变量的可移植性

3.1 char变量的可移植性

  *b的值为这段int[4]的首地址

  定义变量时,只是用关键字char,缺省情况下,根据编译器定义为signed或者unsigned,这样会导致不同机器上char有不同的取值范围。

  定义变量时,只是用关键字char,缺省情况下,根据编译器定义为signed或者unsigned,这样会导致不同机器上char有不同的取值范围。

  **b为这段int[4]首地址指向的内容即为b[0][0]的值

  若显式地将字符声明为signed或者unsigned,则可提高平台可移植性,但机器处理signed和unsigned的能力不同,会导致效率受损。还有不同处理字符的库函数的参数声明为char,显式声明会带来兼容性问题。

  若显式地将字符声明为signed或者unsigned,则可提高平台可移植性,但机器处理signed和unsigned的能力不同,会导致效率受损。还有不同处理字符的库函数的参数声明为char,显式声明会带来兼容性问题。

1.14 指针数组的初始化

  结论:保证可移植性的最佳方法还是定义为char型同时只是用signed
char和unsigned char的交集字符,在进行算术运算时,显式使用。

  结论:保证可移植性的最佳方法还是定义为char型同时只是用signed
char和unsigned char的交集字符,在进行算术运算时,显式使用。

  即用指针来初始化数组

 3.2 运算分量在运算前完成提升**

3.2运算分量在运算前完成提升**

1.15 指向函数的指针

  如果某个算数运算符有一个浮点分量和一个整数运算分量,那么合格整数运算分量在开始运算之前会被转换为浮点类型

  如果某个算数运算符有一个浮点分量和一个整数运算分量,那么合格整数运算分量在开始运算之前会被转换为浮点类型

1.15.1 一个实例:

  表达式先进行类型转换,再计算 z = ( n > 0) ? f : n

  表达式先进行类型转换,再计算 z = ( n > 0) ? f : n

  支持-n进行数字排序的排序函数

  无论n是否为正,z的类型都是float

  无论n是否为正,z的类型都是float

 #include <stdio.h>
 #include <.h>
  MAXLINES 5000 /* max #lines to be sorted */
  *lineptr[MAXLINES]; 
  readlines( *lineptr[],   writelines( *lineptr[],   qsort( *lineptr[],  left,   (*comp)( *,  *  numcmp( *,  * 
 main( argc,  *       nlines; 
      numeric = ; 
      (argc >  && strcmp(argv[], )==          numeric =       ((nlines = readlines(lineptr, MAXLINES)) >=          qsort((**) lineptr,          (int (*)(void*,void*))(numeric ? numcmp : strcmp));
                }          printf(            }

3.3 浮点常量

3.3 浮点常量

  

  浮点常量携程带小数点。如:3.0

  浮点常量携程带小数点。如:3.0

  qsort( *v[],  left,  )
            swap( *v[], ,       (left >= right) 
         ; 
     swap(v, left, (left + right)/     last =      (i = left+; i <= right; i++          ((*comp)(v[i], v[left]) <              swap(v, ++       qsort(v, last+ }

3.4 使用unsigned char类型来接受ASCII码的问题

3.4 使用unsigned char类型来接受ASCII码的问题

 

       unsigned      ( (c=getchar()) !=         }
1 int main()2 {3     unsigned char c;4     while( (c=getchar != EOF){5         putchar;        6     }7     return 0;8 }

1.15.2 指向函数的指针&返回为指针的函数

  EOF宏定义的值为-1,而unsigned char
无法接受-1,所以永远无法到达文件结尾

  EOF宏定义的值为-1,而unsigned char
无法接受-1,所以永远无法到达文件结尾

  指向函数的指针:

3.5 常量表示

3.5 常量表示

  int (*comp)(void *, void *);

  <limits.h>与<float.h>包含了所有这些类型的符号常量以及机器与编译程序的其他性质

  <limits.h>与<float.h>包含了所有这些类型的符号常量以及机器与编译程序的其他性质

  返回为指针的函数:

  long常量要以字母L或l结尾

  long常量要以字母L或l结尾

  int *comp(void *, void *);

  无符号数以u或U结尾

  无符号数以u或U结尾

  C在编译时,每个函数都有一个入口地址,该入口地址就是函数指针所指向的地址

  后缀ul或UL用于表示unsigned long常量

  后缀ul或UL用于表示unsigned long常量

  有了指向函数的指针变量后,可用该指针变量调用函数

  浮点常量的表示方式:123.4或1e-2,无后缀为
double,后缀f或F为float,后缀l或L为long double

  浮点常量的表示方式:123.4或1e-2,无后缀为
double,后缀f或F为float,后缀l或L为long double

1.15.3 函数指针的定义

3.6 进制表示

3.6 进制表示

  1.void (*f)(int x);

  十进制 31 八进制 037 十六进制 0x1F 二进制 0b00011111

  十进制 31 八进制 037 十六进制 0x1F 二进制 0b00011111

  调用(*f)(x);

3.7位模**

3.7位模式表示

  2.typedef int (*fun_ptr)(int,int);

  使用位模式来指定字符对应的ASCII码

  使用位模式来指定字符对应的ASCII码

  fun_ptr max_func = max;

  ASCII码纵向制表符

  ASCII码纵向制表符

  c = max_func(a,b);

  11 ‘\v’

  11 ‘\v’

1.15.4 函数指针数组

  = ‘\xb’ (‘\xhh’ hh为1至多个十六位进制数)

  = ‘\xb’ (‘\xhh’ hh为1至多个十六位进制数)

  1.标准定义:

  = ‘\013’(‘\ooo’为1至3个八进制数)

  = ‘\013’(‘\ooo’为1至3个八进制数)

  int (*op[2])(int,int);

3.8 字符串表示

3.8 字符串表示

  2.强制类型转换

  C语言对字符串长度无限制,但程序必须扫描完整的字符串(’\0’结束符)才能决定这个字符串的长度

  C语言对字符串长度无限制,但程序必须扫描完整的字符串(’\0’结束符)才能决定这个字符串的长度

  定义为普通的int型指针数组

  strlen 返回字符串长度,不包括结束符

  strlen 返回字符串长度,不包括结束符

  在要使用时,强制类型转换为相应的函数指针类型

3.9 枚举类型

3.9 枚举类型

  int *a;

  枚举常量

  枚举常量

  int add(int a, int b);

  1.enum boolean { NO,YES };

  1.enum boolean { NO,YES };

  a = add;

  枚举值从0开始递增 NO = 0 YES = 1

  枚举值从0开始递增 NO = 0 YES = 1

  r = ((int (*)(int,int))(a))(numa,numb);

  2.enum month { JAN = 1, FEB , MAR , APR };

  2.enum month { JAN = 1, FEB , MAR , APR };

1.16复杂指针的理解

  JAN =1 FEB = 2 MAR = 3 APR = 4

  JAN =1 FEB = 2 MAR = 3 APR = 4

  基本形式:

  3.enum escapes { BELL = ‘\a’ , BACKSPACE = ‘\b’ , TAB = ‘\t’ ,
NEWLINE = ‘\n’ };

  3.enum escapes { BELL = ‘\a’ , BACKSPACE = ‘\b’ , TAB = ‘\t’ ,
NEWLINE = ‘\n’ };

  char (*(*x())[])()

  显示指定枚举值

  显示指定枚举值

  *x()如上式5,表示返回值为指针的函数

  尽量用const、enum、inline替换#define,宁可以编译器替换预处理器(EFFECTIVE
C++)

  尽量用const、enum、inline替换#define,宁可以编译器替换预处理器(EFFECTIVE
C++)

  剩余部分表述的就是所返回的指针的类型:

  宏在预处理阶段进行替换工作,它替换代码段的文本,程序运行的时候,宏已经不存在了。而枚举是在程序运行之后起作用的,枚举常量存储在数据段的静态存储区中,宏占用代码段的空间,而枚举除了占用空间,还消耗CPU资源。

  宏在预处理阶段进行替换工作,它替换代码段的文本,程序运行的时候,宏已经不存在了。而枚举是在程序运行之后起作用的,枚举常量存储在数据段的静态存储区中,宏占用代码段的空间,而枚举除了占用空间,还消耗CPU资源。

  返回的指针类型如上式6,返回值char的函数指针数组

  枚举类型值能自动生成,这是相对于#define的优势

  枚举类型值能自动生成,这是相对于#define的优势

  所以,

3.10 自动变量

3.10 自动变量

  x是一个函数,该函数的返回值是一个指向类型为char的函数指针数组的指针  

  只在函数内部定义使用的变量。它只是允许在定义它的函数内部使用它,在函数外的其他任何地方都不能使用的变量。系统自动完成对自动变量存储空间的分配和回收,它的生命周期是从它们被定义到定义它们的函数返回。这个过程是通过一个堆栈机制来实现的,为自动变量分配内存就是压栈,返回就是退栈。

  只在函数内部定义使用的变量。它只是允许在定义它的函数内部使用它,在函数外的其他任何地方都不能使用的变量。系统自动完成对自动变量存储空间的分配和回收,它的生命周期是从它们被定义到定义它们的函数返回。这个过程是通过一个堆栈机制来实现的,为自动变量分配内存就是压栈,返回就是退栈。

  char (*(*x[3])())[5]

3.11 静态变量

3.11 静态变量

   (*x[3])()如上式6为函数指针数组

  不像自动变量使用堆栈机制使用内存,而是在静态存储区分配固定的内存。持续性是程序运行的整个周期。作用域为定义它的函数的内部。

  不像自动变量使用堆栈机制使用内存,而是在静态存储区分配固定的内存。持续性是程序运行的整个周期。作用域为定义它的函数的内部。

  其他的部分描述函数指针数组的返回值

  通过extern访问其他文件中定义的全局变量,如果使用static在函数外面声明变量,则其他文件不允许使用该变量。const
int a声明在函数外也只能在定义它的文件中使用。

  通过extern访问其他文件中定义的全局变量,如果使用static在函数外面声明变量,则其他文件不允许使用该变量。const
int a声明在函数外也只能在定义它的文件中使用。

  所以,x是一个返回值为char[5]的长度为3的函数指针数组

3.12 寄存器变量

3.12 寄存器变量

 

  提高访问效率,具体是否使用寄存器由编译器决定,其地址不能被访问。

  提高访问效率,具体是否使用寄存器由编译器决定,其地址不能被访问。

2.内存布局

 register   ra asm(“ebx”);
1 register int a;2 int ra asm(“ebx”);

2.1数组的内存布局

3.13 易失变量

3.13 易失变量

  int a[10];

  强制访问操作,防止编译器在优化,告诉编译器从内存中取值,而不是从寄存器或缓存。

  强制访问操作,防止编译器在优化,告诉编译器从内存中取值,而不是从寄存器或缓存。

  威尼斯网址开户网站 6

3.14 非自动变量

3.14 非自动变量

  内存是连续分配的相邻区域

  非自动变量包括:全局变量+静态变量

  非自动变量包括:全局变量+静态变量

  int *p = &a;

  非自动变量只初始化一次,在程序开始之前进行,且初始化符为常量表达式,其缺省为0

  非自动变量只初始化一次,在程序开始之前进行,且初始化符为常量表达式,其缺省为0

  p = p + 1;

  自动变量进行其所在函数即初始化,其初始化符可以是任意表达式,未经初始化值为未定义

  自动变量进行其所在函数即初始化,其初始化符可以是任意表达式,未经初始化值为未定义

2.2 大端模式 & 小端模式

 

4.类型转换

  大端模式:即指数据的高位,保存在内存的低地址中,而数据的低位,保存在内存的高地址中

4.类型转换

4.1int到char的转换

  例:

4.1 int到char的转换

  实质上仅仅是一个ASCII码表的映射关系的转换。

  65534(0x0000FFFE)

  实质上仅仅是一个ASCII码表的映射关系的转换。

  C语言内部内置了这种映射关系,
使用char类型管理字符,其实是在管理ASCII码的值,最终输出时完成到字符的映射就行了。

  小端模式(一字节存储单位):

  C语言内部内置了这种映射关系,
使用char类型管理字符,其实是在管理ASCII码的值,最终输出时完成到字符的映射就行了。

  将int赋值给char时,实质上做的是内存截断    

  0x0000    0x0008    0x0010    0x0018

  将int赋值给char时,实质上做的是内存截断    

  例:

    FE          FF         00         00

  例:

1 a = 0x62;2 char c = a;3 pritnf(“%c”,c);

  大端模式:

 a =   c = pritnf(“%c”,c);

  结果为a 

  0x0000    0x0008    0x0010    0x0018

  结果为a 

1 a = 0xFF62;2 char c = a;3 printf(“%c”,c);

    00         00         FF         FE

 a =   c = printf(“%c”,c);

  结果同为a

 

  结果同为a

  ASCII码表 0~255(0~127标准ASCII码 128~255 扩展ASCII码)

3.结构

  ASCII码表 0~255(0~127标准ASCII码  128~255 扩展ASCII码)

4.2 char到int的转换

3.1 结构变量的定义方法**

4.2 char到int的转换

  C语言未指定char类型是有符号还有无符号,所以把char类型的值转换为int类型的值时,视机器不同而有所变化。

  struct {…} x,y,z;

  C语言未指定char类型是有符号还有无符号,所以把char类型的值转换为int类型的值时,视机器不同而有所变化。

  某些机器最左边为1,那么就被转换为负整数,而另一些则提升,在最左边添加0

  语法上与int x,y,z;相似

  某些机器最左边为1,那么就被转换为负整数,而另一些则提升,在最左边添加0

  代码:

3.2 结构变量的定义方法

  代码:

1 int main()2 {3 char a =0xFF;4 int b = a;5 printf(“%d”,b);6 return 0;7 }

  typedef struct{…} myType;

    a =  b = printf(“%   }

  本机测试结果为-1,被转换为了0xFFFFFFFF,被转换为了负整数

3.3 结构变量的初始化


4.3 强制类型转换

  struct point maxpt = {320,200};

  强制类型转换的精确定义:

  强制类型转换的精确定义:

3.4 对结构体的合法操作  

  表达式首先被赋给类型名指定类型的某个变量(会自动构造对应的内存布局),然后再将其用在整个构造所在的位置。

  表达式首先被赋给类型名指定类型的某个变量(会自动构造对应的内存布局),然后再将其用在整个构造所在的位置。

3.5 结构数组的初始化

  参数是通过函数原型声明,那么通常情况下,当函数被调用时,系统对参数自动进行强制类型转换,但是对于printf来说,%f等格式控制符仅仅是决定对对应参数的解释方式,是不会进行强制类型转换的。

  参数是通过函数原型声明,那么通常情况下,当函数被调用时,系统对参数自动进行强制类型转换,但是对于printf来说,%f等格式控制符仅仅是决定对对应参数的解释方式,是不会进行强制类型转换的。

 *= {“auto”,,””,};

  运算前,对于运算分量先把“低”的类型提成为“高”的类型

  运算前,对于运算分量先把“低”的类型提成为“高”的类型

3.6 字节对齐

4.4 float的自动转换

4.4 float的自动转换

  参加博文《关于C语言中结构体中的结构体成员导致的字节对齐问题》

  注意:在表达式中的float类型的运算分量不自动转换成double类型,这与原来的定义不同。一般而言,数学函数要用双精度。使用float类型的主要原因是为了使用较  大的数组时节省存储空间,有时也为了机器执行时间(双精度运算特别费时)

  注意:在表达式中的float类型的运算分量不自动转换成double类型,这与原来的定义不同。一般而言,数学函数要用双精度。使用float类型的主要原因是为了使用较大的数组时节省存储空间,有时也为了机器执行时间(双精度运算特别费时)

 

4.5 unsigned类型的自动转换

4.5 unsigned类型的自动转换

4.其他

  包含unsigned类型的运算分量时,转换规则要复杂一些。主要问题是,有符号值与无符号值之间的比较取决于机器因为它们取决于各个整数类型的大小。

  包含unsigned类型的运算分量时,转换规则要复杂一些。主要问题是,有符号值与无符号值之间的比较取决于机器因为它们取决于各个整数类型的大小。

4.1 逗号表达式**

  若int为16位,long为32位

  若int为16位,long为32位

   逗号运算符,优先级别最低,它将两式连起来,其求解过程先表达式1,后表达式2,整个表达式是表达式2的值

  则-1L<1U 因为unsigned int会转化为signed long类型

  则-1L<1U 因为unsigned int会转化为signed long类型

  (3+5,6+8) = 14

  -1L>1UL,因为-1L会被转化为unsigned long类型      

  -1L>1UL,因为-1L会被转化为unsigned long类型      

  (a = 3*5,a*4) = 60

 

5.运算

4.2 命令行参数

5.运算

5.1 赋值运算

  argc 程序执行时命令行中参数的数目

  赋值运算的结合次序:从右到左

  赋值运算的结合次序:从右到左

  argv 指向字符串数组的指针

5.2 位运算

5.2位运算

4.3 联合

  例:使用bitcount函数统计变量中值为1 的位的个数。

  例:使用bitcount函数统计变量中值为1 的位的个数。

   在单独存储区域中管理不同类型的数据

  方法1:每一位进行匹配移位,直到x为0

  方法1:每一位进行匹配移位,直到x为0

4.4 位字段**

  bitcount( unsigned            ( b = ; x != ; x >>==          ( x &              b ++      }
1 int bitcount( unsigned int x)2 {3     int b;4     for( b = 0; x != 0; x >>== 1)5         if( x & 01)6             b ++;7     return b;8 }
 is_keyword :  is_extern  :  is_static  : 

  方法2:去除变量最右端的1直到变量大小为0

  方法2:去除变量最右端的1直到变量大小为0

  分配空间大小为flags分配字段类型最长的倍数,然后一个字节一个字节地存储。在字节内的存储方式未知,根据不同的编译器决定从右往左还是从左往右。GCC是从右往左。

  bitcount( unsigned       ( b = ; x != ; b++         x &= (x -      }
1 int bitcount( unsigned int x )2 {3     for( b = 0; x != 0; b++ )4         x &= (x -1);5     return b;6 }

 

5.3 表达式先进行类型转换后进行运算

5.3 表达式先进行类型转换后进行运算

. 指针 1.1 自增符的使用 ++*p;//p指向的内容加一
(*p)++; //p指向的内容加一 *p++;//p本身自增 *++p; //p本身自增
因为诸如*和++这样的一元运算符在…

  如计算:

  如计算:

z = ( n > ) ? f : n;
z = ( n > 0) ? f : n;

  无论n是否为正,z的类型都是float

  无论n是否为正,z的类型都是float

5.4 函数调用中变量的求值次序

5.4 函数调用中变量的求值次序

  在函数调用中各个变量的求值次序也是未指定的

  在函数调用中各个变量的求值次序也是未指定的

printf(“%d %d \n”, ++n, power(,n));
printf(“%d %d \n”, ++n, power(2,n));

  错误,不同编译程序会决定是否在power(2,n)之前对n执行++操作

  错误,不同编译程序会决定是否在power之前对n执行++操作

  故改写为:

  故改写为:

++%d %d \n”, n , power(,n));
++n;printf(“%d %d \n”, n , power(2,n));

5.5 加一、减一运算的副作用

5.5 加一、减一运算的副作用

a[i] = i++;
a[i] = i++;

  数组下标是旧值还是新值,编译程序对之可以有不同的解释,并视为不同的解释,产生不同的结果。

  数组下标是旧值还是新值,编译程序对之可以有不同的解释,并视为不同的解释,产生不同的结果。

5.6三元运算符的使用

5.6三元运算符的使用

  三元运算符的使用,可以有效节省代码长度,如: 

  三元运算符的使用,可以有效节省代码长度,如: 

( i =; i <; i++%d %s”,i,(i != ) ? " " : "\n";
for(int i =0; i <5; i++)    pritnf(“%d %s”,i,(i != 4) ? " " : "\n";

 

6.语句

6.语句

6.1if , while , for的条件测试部分真的意思是”非0”

6.1 if , while , for的条件测试部分真的意思是”非0”

6.2 switch语句

6.2 switch语句

  switch语句中case情形的作用就像标号一样,在某个case情形的代码执行完后,就进入下一个case情形执行,除非显示控制转出。

  switch语句中case情形的作用就像标号一样,在某个case情形的代码执行完后,就进入下一个case情形执行,除非显示控制转出。

6.3for循环中可以使用逗号运算符”,”,支持多个表达式

6.3 for循环中可以使用逗号运算符”,”,支持多个表达式

6.4变量和函数可以一起声明

6.4 变量和函数可以一起声明

double sum,atof(char []);
 sum,atof( []);

7.其他

 

7.1printf

7.其他 

  %d 十进制 %o 八进制 %x 十六进制 %f = %lf

7.1(标准库函数)printf 

  加h短整型 加l长整型

  %d 十进制 %o 八进制 %x 十六进制 %f = %lf

  printf中的宽度、精度可由*号来控制

  加h短整型 加l长整型

  例如: 

  printf中的宽度、精度可由*号来控制

printf(“%.*s”,max,s);

  例如: 

  %后跟-符号表述左对齐,

printf(“%.*s”,max,s);

  如:

  %后跟-符号表述左对齐,

int a = 1;printf(“%-4d”,a);//输出在最左,无法展示出4位字宽

  如:

  定义如printf这样的带有变长参数表的函数时,参数表至少有一个参数

 a = %-4d”,a);
void minprintf( char *fmt,...);

  定义如printf这样的带有变长参数表的函数时,参数表至少有一个参数

7.2定义与声明的区别

 minprintf(  *fmt,...);

  定义:变量建立或分配存储单元的位置

7.2 定义与声明的区别

  声明:指明变量性质的位置,不分配存储单元

  定义:变量建立或分配存储单元的位置

7.3变量的定义不是只能出现在函数开始的部分

  声明:指明变量性质的位置,不分配存储单元

7.4数组初始化

7.3 变量的定义不是只能出现在函数开始的部分

  数组初始化,没有被初始化的部分自动置0

7.4 数组初始化

  字符数组的初始化   

  数组初始化,没有被初始化的部分自动置0

char pattern[] = “ould”;= char pattern[] = {’o’,’u’,’l’,’d’,’\0’};

  字符数组的初始化   

7.5 宏定义

 pattern[] ==  pattern[] = “’o’,’u’,’l’,’d’,’\’”;

  7.5.1 宏定义中的加一、减一

7.5 宏定义

#define max =  >  : 

  7.5.1 宏定义中的加一、减一

  该宏对于传入的i++情形,会加两次

 max(A,B) = ((A) > (B) ? (A) : (B))

  7.5.2 宏定义中的字符串

  该宏对于传入的i++情形,会加两次

  参数名以#为前缀,那么它们将被由实际参数替换的参数扩展成带引号的字符串

  7.5.2 宏定义中的字符串

#define dprint printf(#expr “ = %g \n”, expr);

dprint(x/y);printf(“x/y”” = %g \n”, x/y);

  参数名以#为前缀,那么它们将被由实际参数替换的参数扩展成带引号的字符串

  输出结果为:x/y = ?

 dprint(expr) printf(#expr “ = %g \n”, expr);

dprint(x//y”” = %g \n”, x/y);

  7.5.3##为宏扩展提供了一种连接实际参数的手段

  输出结果为:x/y = ?

#define paste(front,back) front ## backpaste(name,1) ; //得到name1

  7.5.3 ##为宏扩展提供了一种连接实际参数的手段

  7.5.4#if语句中包含一个常量整数表达式(其中不得包含sizeof,强制类型转换运算符或枚举常量),在#if语句中可以使用一个特殊的表达式defined。

 paste(front,back) front ## back) ; 
#if !defined    #define HDR#endif#if ....#elif ....#else..#endif

  7.5.4 #if语句中包含一个常量整数表达式(其中不得包含sizeof,强制类型转换运算符或枚举常量),在#if语句中可以使用一个特殊的表达式defined(名字)。

  两个特殊的表达式

 !defined(EDR)
     HDR


 .. ..
#ifdef  = #if defined#ifndef = #if !defined

  两个特殊的表达式

7.6取地址运算符只能应用于内存对象,不能对表达式、常量或寄存器变量进行操作

#ifdef  =  defined(***)=  !defined(***)

7.7 const限定符

7.6 取地址运算符只能应用于内存对象,不能对表达式、常量或寄存器变量进行操作

  1. 用const修饰一般变量

7.7 const限定符

  const修饰的变量必须在声明的时候进行初始化,一旦一个变量被const修饰后,在程序中除初始化外对这个变量进行的赋值都是错误的。

  1.      用const修饰一般变量

  2. const与指针搭配使用

  const修饰的变量必须在声明的时候进行初始化,一旦一个变量被const修饰后,在程序中除初始化外对这个变量进行的赋值都是错误的。

  指针常量,即指针本身的值不可改变

  2.      const与指针搭配使用

  常量指针,即指针指向的变量的值是不可以改变的

  指针常量,即指针本身的值不可改变

  const int *p 和int const *p1;const形容的指针指向的内容

  常量指针,即指针指向的变量的值是不可以改变的

  int * const p2 = &b; const形容的是指针本身

  const int *p 和int const *p1;const形容的指针指向的内容

  const修饰的变量需要初始化

  int * const p2 = &b; const形容的是指针本身

  3. 作为函数参数

  const修饰的变量需要初始化

  4. 节省空间,避免了不必要的内存分配

  3.      作为函数参数

  const定义常量从汇编角度来看,只是给出了对应的内存地址,而不是像#define一样给出了立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

  4.      节省空间,避免了不必要的内存分配

  5.
编译器通常不为普通const常量分配存储空间,而是将他们保存在符号表中。这使得它成为一个编译期间的常量。没有了存储与读内存的操作,使得它的效率也很 高。

  const定义常量从汇编角度来看,只是给出了对应的内存地址,而不是像#define一样给出了立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

  6. 阻止用户修改函数返回值

  5.     
编译器通常不为普通const常量分配存储空间,而是将他们保存在符号表中。这使得它成为一个编译期间的常量。没有了存储与读内存的操作,使得它的效率也很 高。

  7. 结构体中const成员变量的初始化

  6.      阻止用户修改函数返回值

struct A s = {10,2};

  7.      结构体中const成员变量的初始化

  与结构体的初始化相同

 A s = {,};

  8.  const是只读变量

  与结构体的初始化相同

const int n= 5int a[n];

  8.  const是只读变量

  错误。const是只读变量,而非常量。

  n= 
 a[n];

  9. const变量 & const限定的内容

  错误。const是只读变量,而非常量。

typedef char * pStr;char string[4] = “abc”;const char *p1 = string;const pStr p2 = string;p1++;//正确p2++;//错误

  9.      const变量 & const限定的内容

  分析:

typedef  * [] =  *p1 =  pStr p2 = ++;
p2++;

  1)const使用的基本形式:const char m 限定m不变

  分析:

  2)替换1式中的m,const char
*pm;限定*pm不可变。当然pm是可变的,因此p1++是正确的。

  1)const使用的基本形式:const char m 限定m不变

  3)替换1式char const newType
m;限定m不可变,问题中的pStr是一种新类型,因此问题中的p2不可变。p2++是错误的。

  2)替换1式中的m,const char
*pm;限定*pm不可变。当然pm是可变的,因此p1++是正确的。

  10. 字符串常量与字符数组

  3)替换1式char const newType
m;限定m不可变,问题中的pStr是一种新类型,因此问题中的p2不可变。p2++是错误的。

  char *c = “Hello World”;字符常量

  10.   字符串常量与字符数组

  char c[] = “Hello World”;字符数组

  char *c = “Hello World”;字符常量

  字符串常量存在静态存储区(只读的,rodata段中)

  char c[] = “Hello World”;字符数组

  字符数组放在动态存储区

  字符串常量存在静态存储区(只读的,rodata段中)

  所以字符串常量不能被修改 c[1] = ‘a’

  字符数组放在动态存储区

  所以字符串常量不能被修改 c[1] = ‘a’

 

C programming
language》,果然如听说的一样,讲解基础透彻,案例简单典型,确实自己C语言还有很多细节点不是很清楚。…

相关文章