@OrionPaxxx
2017-03-12T09:13:15.000000Z
字数 15710
阅读 1493
c
预编译代码文件是编译器进行了去注视、头文件包含和宏替换后的源代码文件,后缀为.i;它有助于查看宏替换后的程序代码,分析程序代码错误;
在VC中只需在C/C++-->Command Line中追加上/P命令参数就可以了;
这样Compile某个.cpp文件后,就能生成对应的预编译文件了
#include甚至不是c语言的语句#符号表明这一行是在编译器接手之前由C预处理器处理的语句(预处理器指令,详情了解‘c预处理器和c库’
int指明mian函数的返回值类型是一个整数,void表示不接受任何参数。
非标准形式如:
mian()
void main()
应尽量使用标准形式
c99标准允许一个标识符最多可以有63个字符,不会识别额外的字符,
例如:
如果一个系统最大字符数为8,则shakespare和shakespencil奖被识别为同一个名字
16进制:0x或者0X,%x
8进制:0,%0
10进制:%d
int16_t:16位有符号整数类型
uint32_t:32位无符号整数类型
intmax_t:最大有符号整数类型
uintmax_t:最大无符号整数类型
最小长度类型(minimum width type)
int_least8_t:可以容纳8位有效符号数的那些类型中最小的一个
fastest minimum width type(最快最小长度类型)
int_fast8_t:系统中对8位有符号数而言最快的的整型类型的别名
上溢:printf输出inf或者infinity
下溢:0.1234e-10除以10得到0.0123e-10,损失以为有效数字
还有一个特殊的浮点值:NaN(not a number):例如asin()输入参数大于一时
,printf将此值显示为nan或者NaN
三种复数类型:
float_Complex
double_Complex
long double_Complex
三种虚数类型
float_Imaginaty
double_Imaginary
long double_Imaginary
如果包含了complex.h,则可用complex代替_Complex,imaginary代替_Imaginary
整型类型:int,long,short,unsigned int,unsigned long int,unsigned short int
浮点类型:float,double,long double
字符型:char,signed char,unsigned char
其他:_Bool, _Complex , _Imaginary
#include<stdio.h>int main(void){int a=4;int b=5;float c=5.0;printf("%d\n",a,b);printf("%d %d\n",a);printf("%d %f\n",c,a);getchar();}
输出结果为:
4
4 20451598
0 0.000000
运行程序不会报错,%d不会将float转换为int,%f不会将int转换为float。再如:以%u输出一个负数会得到一个错误的结果。(但计算过程中会按照自动类型转换规则转换)。
一个好的编程习惯是让说明字符和要输出的值做到以下匹配:
1.数据类型匹配
2.字节数匹配(printf从堆栈中把数读出来时,会按照说明符指出的字节数读取,如果字节数不匹配可能导致只读取数据的部分或者把其他数据部分一同读入)
#include<stdio.h>void fuck(void);int main(void){int haha;scanf("%d",&haha);printf("%c",haha);getchar();fuck();}void fuck(void){getchar();}
或者
#include<stdio.h>double haha(double n,float m);int main(void){...;...;return 0}double haha(double n,float m){mul=n*m;return mul;}
例子:
char name{[40}];//声明创建一个有40个存储单元(或元素)的数组,每个单元可以存储一个char类型的值
sizeof:以字节为单位给出数据大小(查看数据占了多少个字节)
strlen:以字符为单位给出字符串长度
#define NAME value//没有分号,一个好的习惯是用大写来表示常量,用小写来表示变量
把一个变量声明转化换成常量声明
const int haha=123;//使哈哈成为一个只读值
1.如果使用scanf来读取之前讨论过的某种基本变量类型的值,在变量前加&
2.如果使用scanf来把一个字符串读进一个字符数组中,不加&
3.scanf函数需要严格按照格式字符串的形式输入如:
scanf("%d,%d",ha,ha);//键盘需键入中间的逗号scanf("%d%d",ha,ha);//键盘输入需用空白符将2 个数据隔开
y=2;n=3;nextum=(y+n++)*6;
n的值为:30(先使用再增加)
| 表达式 | 值 |
|---|---|
| -4+6 | 2 |
| c=3+8 | 11 |
| 5>3 | 1 |
| 6+(c=3+8) | 17 |
(不建议使用合法但是奇怪的表达式)
mice=1.6+1.7;mice=(int)1.6+(int)1.7;
p115
c99提供一个stdbool.h头文件,包含这个头文件可以使用bool来代替_Bool,并且把true和false定义为1和0的符号常量,在程序中包含这个头文件可以写出与C++兼容的代码,因为C++把bool,true和false定义为关键字。
p137
for(A;B;C)
A:初始化,在循环开始时执行一次
B:判断条件,在每次执行循环前对其进行求值,为假时退出
C:在每次循环结束时进行计算
可以让或多个表达式为空但是不能省略逗号
事实上,A,B,C几乎可以是任何表达式,但要记住他们执行的时间点,以及表达式B为假时退出循环。
常在for循环中使用,拓展for循环的灵活性。逗号是一个顺序点(从左到右进行计算),其值为最后一个表达式的值。
do{...}while(...);
一个例子
while((ch=getchar())!='n')
对上述程序说明:计算机首先7调用getchar函数,然后将其返回值赋给ch,而赋值表达式的值就等于表达式左侧数的值。
ch=getchar();putchar(ch);
getchar():从键盘读入单个字符,不接受参数,其返回值为接受到的字符,ch=getchar()的值(为一个赋值表达式的值)为ch的值,也就是getchar的返回值。
例子:如果isalpha()函数的参数是个字母,则它返回一个非零值
&& 同and
||同or
!同not
expression1 ? expression2 : expression3
如果expression1为真,则整个表达式的值和expression2的值相同,如果expression1为假,则整个表达式的值和expression3的值相同
switch(integer expression or integer){case constant1:statement;case constan2:statement;......default:statement;}
switch 后的括号里和case后的表达式只能是整型常量(或字符型)和整型表达式。
如果没有break语句每一个case都将被扫描,而每一个匹配的case都将被执行。如果没有匹配的case,将执行default后的语句,如果没有default,程序将跳转到switch语句之后的语句。
可以使用多重标签:
case 1:case 2:statement;
goto part2;......part2:statement;
判断是否到达文件末尾:
while((ch=getchar())!=EOF)putchar(ch);
计算机术语,缩写通常为EOF(End Of File),在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流.在C语言中,或更精确地说成C标准函数库中表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
在C语言中它是一个无类型指针,并且值为0。NULL的出现是一种约定俗成,事实上它不是C语言中的关键字;把一个指针赋值为NULL,通常的说法是“将指针悬空”。这样,指针就无法再进行任何数据访问了。
参见:百度百科--NULL
#include<stdio.h>int main(void){int * ar;ar=NULL;printf("%p",ar);}
上面程序输出为:
00000000
在linux下,下面的命令将2个文件编译在一起并生成可执行文件a.out
将常量和函数原型放在头文件中,
#include“xxxx.h"中双引号表示被包含的文件位于当前工作目录下
按照教材上的例子,一个c文件存放main(),另一个c文件存放函数定义,再用一个头文件存放常量定义和函数原型(c文件中要将头文件#include进去)
z=*x //将x指向的赋给z
改变调用函数中的变量
#include<stdio.h>void change(int u,int v);int main(void){int x=1;int y=2;printf("x=%d,y=%d,&x=%x,&y=%x\n",x,y,&x,&y);change(x,y);printf("x=%d,y=%d,&x=%x,&y=%x\n",x,y,&x,&y);}void change(int x,int y){int temp;temp=x;x=y;y=temp;printf("x=%d,y=%d,&x=%x,&y=%x\n",x,y,&x,&y);}
运行上面程序,结果如下:
x=1,y=2,&x=7c7291d0,&y=7c7291d4
x=2,y=1,&x=7c7291ac,&y=7c7291a8
x=1,y=2,&x=7c7291d0,&y=7c7291d4
main()中的值并未得到交换,因为change中的中使用的变量独立于main()中的变量,有独立的地址。问题出现在把执行结果传递个给main()的过程中,结果并未传递给main()。我们可以改变main()调用子函数的方式:
x=change(x,y);
同时在子函数末尾加上:
return x;//return(x);也可以
将x的值传递回去,但是y值依然没有改变!要想main()中x,y的值都改变,就需要用到指针!
指针是一个数值为地址的变量
x = &y;//我们称x指向y,x为指针变量,而 &y 是一个常量。。这是x的值是y的地址
指针是一种新的数据类型
声明指针变量
int * pi ; /* pi 是指向一个整形变量的指针*/char * pc; /* pc是指向一个字符型变量的指针*/flaot * a, * b ; /* a 和b是指向浮点变量的指针*/
#include<stdio.h>void change(int * u,int * v);void main(void){int x=1;int y=2;printf("%d,%d\n",x ,y);change(&x,&y);printf("%d,%d\n",x ,y);}void change(int * u,int * v){int temp;temp=*u;*u=*v;*v=temp;}
上面程序输出结果为:
1,2
2,1
量有常量和和比那量之分。对于每一个变量,都有与之对应的数据类型/变量名/值和地址等,而一个变量的地址是唯一确定的。
1.理解函数之间的信息传递机制,也就是说要明白函数参数以及返回值是如何工作的
2.因为函数的参数和其他局部变量是函数所私有的,所以在不同函数中声明的同名变量是完全不同的
3.任何函数不能直接访问其他函数中声明的变量(main()也不能),这种操作的局限性有助于保护数据的完整性
4.当确实需要在一个函数中访问其他函数声明的变量时,可以使用指针参数
对于python也具有同样的性质,我们看看下面的程序:
a=12b=45print a,bdef exchange(a,b):temp=aa=bb=tempprint a,bexchange(a,b)print a,b
其输出结果为:
12 45
45 12
12 45
但是如果用python面向对象编程却能过实现:
class test:def __init__(self):self.a=12self.b=45print self.a,self.bdef exchange(self):temp=self.aself.a=self.bself.b=tempprint self.a,self.bdef printf(self):print self.a,self.bss=test()ss.exchange()ss.printf()
上面程序输出为:
12 45
45 12
45 12
char haha[50];
int haha[]={1,2,3,1,2,1,41,5,2};
对数组使用const方法创建只读数组
#define NUMBER =12;
const int days[NUMBER]={1,2,3,1,5,4};//数值数目少于数组元素数目时,多余的数组被初始化为0.
但是如果不对数组进行初始化,则数组内存储的元素不定。(与存储类有关,之前的所有变量和数组都是制动存储类型,有些存储类型会把没有初始化的变量和数组的存储单元设置为0.
c99新标准允许对可以对指定项目初始化:
int days[6]={[4]=3};
看看下面的示例程序:
#include<stdio.h>#define NUMBER 10int main(void){int i=0;int haha[NUMBER]={31,28,[4]=31,30,31,[1]=29};for(i;i<=NUMBER;i++){printf("%d %d\n",i+1,haha[i]);}}
其输出结果为:
1 31
2 29
3 0
4 0
5 31
6 30
7 31
8 0
9 0
10 0
11 -1964437760
做如下说明:
1.[4]=31,30,31对haha[4],haha[5],haha[6]进行赋值
2.多次初始化,最后一次有效
:只能对单个元素赋值
const int haha[3][23][34];//定义三维数组
下面是一个对二维数组的初始化:
int haha[2][3]={{1,2,3},{3,4,5},{5,6,7,}};
在c99之前放括号[]内只能是整型常量或者整型表达式,而且必须大于0(等于0也不行),下面的语句在c99之前不行:
int n=23;int haha[n];
数组名是该数组首元素的地址:
haha==&haha[0];
在c中对一个指针加一的结果是对该指针增加一个存储单元;对于数组而言,地址会增加到下一个元素的地址,而不是下一字节,这就是为什么在声明指针变量时必须声明他所指向的对象的类型。
定义指针:
1.指针的数值就是它所指向对象的地址,对于含有多个字节的数据类型,对象的地址
2.在指针变量前使用运算符*可以得到指针指向对象的值
3.对指针加一,等价与等价于对指针的值加上它指向对象的字节大小
dates=&date[0];date+2==&date[2];//其值为真*(date+2)==date[2];//其值为真
在函数原型和函数定义中可以用ar[]代替* ar
在对数组进行操作的函数中,下面4种函数原型等价
int test(int * ar);int test(int *);int test(int ar[]);int test(int []);
由于数组名只包含了数组首地址和数组类型,因此在传递数组参量时常常需要再传递一个代表数组元素个数的整数参量。
在c中ar[i]和*(ar+i),等价,而且不论ar是数组名还是指针变量都能工作,但是只有当ar是一个指针变量时,car能使用
ar++这样的表达式。(不能对数组名进行赋值)
对诸如int之类的基本数据类型:
传递int数值,起保护作用,传递int指针&int,可以修改相应的值
传递数组参量,只能使用指针,若过要保护数组参量,对形式参量使用const:
int haha(const int ar[]);
const int days[];//定义数组常量int rates[3]={1,2,3};const double * pc=rates;/*定义指向常量的指针,不能用于修改数值,但是诸如rates[1]之类的可以修改数值。可以使用pc++让pc指向其他地址*/double * const pd=rates;//不能使用pd++使pd指向其他地址const double * const pf;//不能修改数值,也不能指向其他数值
#include<stdio.h>int main(void){int haha[2][3]={{1,2,3},{4,5,6}};int i;int j;printf("%p\n",haha);printf("%p\n",haha[0]);printf("%p\n",&haha[0][0]);printf("%p\n",&(haha[0]));printf("%d\n",haha[0][0]);}
输出结果如下:
0x7ffc64f3f7900x7ffc64f3f7900x7ffc64f3f7900x7ffc64f3f7901
再看另外一个例子:
#include<stdio.h>int main(void){int haha[3]={1,2,3};printf("%p\n",haha);printf("%p\n",&haha);}
输出结果为:
0x7fff45d08f000x7fff45d08f00
对haha+1和对haha[0]+1的结果不同
int (* pc)[2]//pz指向一个包含2个int值的数组
int * pc [2]//这样会创建2个指向单个int值的指针
可以将一个int值赋给一个double变量,但是对于指针不行。
在函数原型和函数定义中可以使用如下形式定义一个指向数组的指针:
void haha(int (* pc)[3]);void haha(int pc[][3]);//在pc是形式参量是可以用这种形式/*在函数原型中,还可以做如下简化:*/void haha(int [][3]);
还可以使用typedef定义多维数组
typedef int haha[3];//haha是包含3个int的数组typedef haha lolo[4];//lolo是包含3个haha的数组/*下面几种形式等价*/void fick(lolo pc);void fick(int (* pc)[3]);void fick(int pc[][3]);void fick(int pc[4][3]);//有效,但编译器忽略4
声明多维数组方法如下:
int haha(int pc[][2][3][4]);//除了最左边的方括号可以留空,其余都应该填写数字int haha(int (*pc)[2][3][4]);//与上面等效/*pc指向一个2*3*4的int数组*/
(其大小可以有变量来指定)
int x=3;int y=4;double haha[x][y];//一个变长数组
下面是一个复合文字,
(int [3]){1,2,3};
可以简写为:
(int []){1,2,3};
可以直接作为实参,或者将赋值给一个指针变量
int * pc;pc=(int [3]){1,2,3};
在多维数组的情况下,有如下示例:
int (* pc)[3];pc={{1,2,3},{4,5,6}}
#include<stdio.h>int main(void){printf("%s,%p,%c\n","we","are",*"qwer");}
输出结果为:
下面的程序:
we,00D65860,q
#include<stdio.h>int main(void){char haha[]="motherfucker";printf("%s,%p\n",haha,haha);}
输出结果为:
motherfucker,012ff830
char haha[]="qwer";//编译器自动补上'\0'.char haha[]={'q','w','e','r','\0');//如果没有结尾的'\0',只是字符数组而不是字符串。
注意区分'\0',0和'0'。
可以使用下面2中表达式:
char * haha="qwer";//指针变量,后面的编写中可以使用诸如haha++这种表达式,让指针只想别处char hehe[]="qwer";//,常量数组名,后面的编写不能使用诸如hehe++这种表达式。数组的元素是变量,但是数组名是常量haha=hehe;//有效hehe=haha;//无效/* ps:,声明指针与声明其他数组数组不同,声明数组只能使用数组名,而不能使用指针 */
除此之外,我们还要注意下面的区别:
如果一数组名的形式声明:
#include<stdio.h>int main(void){char ha[]="qwer";char he[]="qwer";printf("%p,%p",ha,he);}
结果为:
010ffe04,010ffdf4
但是如果用指针形式声明:
#include<stdio.h>int main(void){char * ha="qwer";char * he="qwer";printf("%p,%p",ha,he);}
输出结果为:
00b65858,00b65858
编译器可能使用同意个单个的拷贝来表示所有相同的字符串,让不同的指针变量都指向同一个字符串。
因此,修改一个字符串可能导致所有用了这个字符串的地方都被修改,为避免这种情况,可在前面加上const修饰符。
声明一个字符串数组方法:
char *haha[3]={"qwer","wtf","weq"};//声明包含三个指针的数组,可以char (*ha)[3]={"qwer","wtf","weq"};//为声明一个指向有三个元素的数组的指针,不可以char hehe[][3]={"qwer","wtf","weq"};//只能建立所有字符串等长的数组
haha数组并不存放字符串,只是存放了字符串的地址,因此,可以用下面的语句来访问字符
char qwer;qwer=*ha[0];qwer=[0][0];
haha创建指向3个字符串的指针的数组,而hehe创建的是char数组的数组。haha存放3个地址,hehe存放3个完整的字符数组
看看下面这个程序:
#include<stdio.h>int main(void){char * ar = "dont be a fool";char * ha;ha=ar;printf("%s,%p,%p\n",ar,&ar,ar);printf("%s,%p,%p\n",ha,&ha,ar);}
输出为:
dont be a fool;0095ff08,013c5858
dont be a fool;0095fefc,013c5858
char * name;scanf("%s",name);
上面的程序,不可取,因为name是一个没有初始化的指针,可能指向任何位置,可能导致程序异常终止。
最简单的方法是在声明中确定其大小:
char * name[45];
现在name是一个已分配81字节存储块的地址,另一种方法是使用c库中分布存储空间的函数(12章)。
为字符串语录空间后,就可以读入字符串了。
c库提供三个读取字符串的函数:scanf(),fgets(),gets()
void fit(const * string){if(strlen(string)>8)printf("qwer");}
接受2个字符串参数,将第二个字符串的一份拷贝添加到第一个字符串的末尾,使第一个成为一个新的字符串,而地位个不变。
strcat()函数不检查第一个函数是否能够容下第二个字符串,,strncat需要另一个参数来指明最多允许添加字符的个数。
程序读取命令行的附加项
下面是一段程序
#include<stdio.h>int main(int argc,char * argv[]){int counter;for(counter=0;counter<argc;counter++){printf("%s\t",argv[counter]);}}
在命令行中:
gcc test.c
得到a.exe文件
然后:
a.exe q w e r t y
输出为:
a.exe q w e r t y
main允许接收2个参数(或者不接收),一个为输入命令行的字符串个数,通常称为 argc(argument count)另一个为指向字符串的数组指针argv。命令行中的每个字符串存储到内存中,并且为其分配一个指针。系统使用空格来判断字符串的结束和开始。
代码块作用域:仅在定义它的代码块({}包括的一块)中起作用
函数原型作用域:从变量定义处一直到原型声明的尾部
文件作用域:从它的定义处到包含该定义的文件结尾处都是可见的
#inculde<stdio.h>int haha; //具有文件作用域的变量,在main和qwer中都能使用void qwer(void);int main(void){...}
函数作用域:只适用于goto语句使用的标签,一个函数中的goto标签对该函数中任何地方的代码都是可见的,无论标签出现在那个代码块中。
空连接(no linkage):具有代码块作用域或者函数原型作用域的变量有空连接,意味着他们由定义他们的代码块或者函数原型所私有。
外部链接(external linkage):具有文件作用域的变量可能有内部链接或者外部链接。一个具有外部链接的变量可以在一个多文件程序的任何地方使用。
内部链接(internal linkage):具有文件作用域的变量可能有内部链接或者外部链接。一个具有内部链接的变量可以在一个文件的任何地方使用。
int haha;//文件作用域,外部链接static int dodger;//文件作用域,内部链接int main(void){...}
静态存储时期(static storage duration):在执行程序期间一直存在,具有文件作用域的变量具有静态存储时期,
自动存储时期(automatic storage duration):具有代码块作用域的变量一般具有自动存储时期,在程序进入定义这些变量的代码块时,程序为其分配内存;在退出这个代码块是时,分配的内存被释放。(在一个函数调用结束后,它的变量可以用来存储下一个被调用函数的变量。
其他内容参见教材P324
示例程序:
#include<stdio.h>int main(void){int x=10;printf("%d\n",x);{int x=20;printf("%d\n",x);}printf("%d\n",x);while(x++ <= 13){int x=100;printf("%d\n",x);}printf("%d\n",x);}
输出为:
10
20
10
100
100
100
100
15
#include<stdio.h>int main(void){int i=10;printf("%d\n",i);for(int i=0;i<=3;i++,printf("%d\n",i))//其中定义的变量i不属于for循环的子代码块,而属于main{int i=34;printf("%d\n",i);}}
输出为:
10
34
1
34
2
34
3
34
4
但是如果在之代码块中没有定义同名变量则不会覆盖:
#include<stdio.h>int main(void){int i=10;printf("%d\n",i);for(int i=0;i<=3;i++,printf("%d\n",i)){printf("%d\n",i);}}
输出结果为:
10
0
1
1
2
2
3
3
4
无{}的代码块:
#include<stdio.h>int main(void){int x=10;printf("%d\n",x);for(int x=1;x<=2;x++)printf("%d\n",x);printf("%d\n",x);}
输出为:
10
1
2
10
**注意在for循环中定义变量(int x=1),为c99特性,在编译时要在命令行中加入: **
-std=c99
register int i;//register只能用于具有代码块作用域的变量
(可能使程序运行更快,但是不能位寻址),
static int i;
当包含这些变量的函数完成工作时,变量不消失,从一次函数调用到下一次,计算机都记录它们的值。
只能用常量表达式来初始化文件作用域变量!!
在所有函数外定义的变量为具有外部链接的静态变量:
#include<stdio.h>int i;//定义声明int main(void){extern int i;/*引用声明,使用在其他地方定义的变量,不会引起空间分配,不要使用extern进行外部定义,只能用他引用一个已经存在的外部定义。*/...}
下面的做法不能用于定义具有外部链接的静态变量:
extern int i;//应用声明,没有定义和分配内存,编译器假定tern的真正定义在程序的某个地方。int main(void){...}
一个外部变量只能初始化一次,下面的语句是错误的
extern int i =10; //错误
static int i;//具有内部链接的静态变量int j;//外部链接int main(void){extern int i;//表示main在使用一个全局变量,但是i任然具有内部链接。extern int j;//表示main在使用一个全局变量...}
除了一个定义声明外,其他的所有声明必须使用关键字extern,并且只有在定义声明中可以初始化。多文件编译时,除非在第二个文件中也声明了该变量(用extern),否则在一个文件中定义的外部变量不能用于第二个文件,但是若在头文件中做了如下声明:extern int i;则不用再在第二个函数中使用引用声明extern
double gamma(void);/*默认为外部*/static double delta(void);//只能在本文件夹使用,可以在其他文件夹创建同名函数extern double beta();//通常用来引用声明其他文件夹中定义的函数
( p346,没看以后看吧)
下面的语句子编译时就已经分配了内存:
int planets[100];
可以在程序运行是分配更多内存,当然我们可以使用VLA的方法:
int m;...int planets[m];
我们还可以用 stdlib.h 里面的函数:
接受参数:所需内存的字节数
返回值:分配的内存的第一个字节的地址,可以将赋值给一个指针变量,其为指向void的指针类型(通用类型),一般需要把返回值的类型指派为适当的类型。
double * pc;pc=(double *)malloc(n*sizeof(double));/*n为之前声明过的变量,pc为指向double的类型,可以使用诸如pc[2]这样的语句访问内存*/
通常对应一个malloc(),应该调用一次free(),其接受参数为之前malloc()分配的地址,它释放先前分配的内存,但是不能用free()释放通过其他方式(例如声明一个数组)分配的内存,下面是一个例子:
int * pi;pi=(int *)malloc(4*sizeof(int));...free(pi);
int * men;men=(int *)calloc(100,sizeof(int));//建立100个int所占字节大小的单元
对于多维数组可以参看下面的程序:
int n;int m;int ar[n][m];int (*p1)[m];p1=(int (*)[m])malloc(n*m*sizeof(int));//注意对* 使用了(),
三种内存:
1.具有外部/内部/空链接的静态变量
2.自动变量
3.动态分配的内存:在malloc或相关函数处产生,在free()处消失,由程序员控制而不是一系列固定的规则控制。
难点:##运算符 和#pragma
三种预处理器功能:1.宏定义 2.文件包含 3.条件编译
其中宏定义是替换,不做计算,也不做表达式求解
#define TWO 2#define FOUR TWO*two
在#define中使用参数
#define SQUARE(x) X*X
在程序中可以这样使用
z=SQUARE(2);
注意:在程序中凡是出现SQUARE(x)的地方将用x*x替换,所以SQUARE(4+2)被替换成:4+2*4+2,可以用添加括号的方法得到我们想要的结果:SQUARE(x) (x)*(x)
C/C++不允许重复#define同一个常量如:
#define SIX 2*3#define SIX 4 //非法的,报错
可以使用#undef取消常量定义
#define SIX 2*3#undef SIX#define SIX 4
头文件的有些语句在一个文件中只能出现一次,如结构类型的声明,
通常使用下面方法避免重复包含带来的麻烦
#ifndef STDIO_H_#define STDIO_H_//头文件的内容#endif
很想常规C中的语句
#if defined(SYS) //defined 为预处理器运算符,判断其参数是否已经用#define定义过#include"head1.h"#elif defined(HAHA)#include"head2.h"#else defined(DUCK)#include"head.h"#endif
__DATE____LINE____TIME____FILE____STDC____STDC_HOSTED____STDC_VERSION__ //相关含义参见C primer plus 474页
预定义宏可以在原程序中直接使用,如:
int main(void){std::cout << __TIME__;}
...
#if __STD_VERSION__ !=199901L#error NOT C99 //使处理器发出一条错误消息,肯恩恶搞的话,编译过程应该中断#endif
编译指示是机器或者操作系统专有的,对于每个编译器都是不同的
在现代编译器中,可以用命令行参数或者IDE菜单修改编译器的某些设置,也可以用#pragma将编译器指令置于源代码中如:
#pragma c9x on //启用对C9X的支持