声明,本文来自中国mooc中的翁凯C语言总结
第一章介绍
变量定义
- 变量定义的一般形式为:<类型名称><变量名称>
- 变量需要一个名字,变量的名字是一种表示符,意思是用来识别不同的标识符
- 标识符的基本构造规则为:标识符只能由字母、数字和下划线组成,标识符不能以数字开头,关键字(保留字)不能做标识符。
变量赋值和初始化
- 变量在被使用之前要赋一次值,这就是初始化
- 所有的变量在使用之前必须定义或声明,所有变量必须具有确定的数据类型
常量
- 固定不变的常数,直接写在程序里,我们称之为直接量
- const int coiunt = 100;
- const是一个修饰符,加在int前面,表示初始化后就不可以再修改
浮点数
- 两个整数的运算结果只能是整数
- 浮点数是带小数点的数值,表示小数点是可以浮动的,与定点数小数点永远出现在第几位不一样
- 浮点数和整数同时进行运算时,c会将整数转换成浮点数
- 用scanf输入的时候float用%f,double用%lf;用printf输出的时候无论是float类型还是double类型,输出都需要用%f,在有些系统中用%lf也不会出错,但是尽量还是按照标准来。
运算符的优先级
复合赋值
第二章介绍
关系运算符优先级
所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高。
- 7>=3+4
- int r=a>0
#include<stdio.h>
int main()
{
printf("%d\n",7>=3+4);
return 0;
}
#include<stdio.h>
int main()
{
// printf("%d\n",7>=3+4);
int a=5;
int r=a>0;
printf("%d\n",r);
return 0;
}
二者的输出结果都为1
判断是否相等的 == 和 != 的优先级比其他的低,而连续的关系运算是从左到右进行的
- 5>3 == 6>4
- 6>5>4
- a == b == 6
- a == b>0
#include<stdio.h>
int main()
{
int a=3,b=5;
printf("%d\n",5>3 == 6>4);
printf("%d\n",5>6>4);
printf("%d\n",6>5>4);
printf("%d\n",5>6>0);
printf("%d\n",a==b==6);
printf("%d\n",a==b>0);
return 0;
}
if和else语句
一个基本的if语句由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“{}"之间的若干条语句。如果表示条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句。
如果没有一对大括号“{}",那么后面跟上一条语句之后加上分号表示if语句的结束。else也是同样的道理。但else需要注意的是else总是和最进的if进行配对,除非加了大括号,特别说明的是缩进格式是不能够暗示else匹配的。
switch-case语句
switch语句可以看作是一种基于计算的跳转,计算控制表达式的值后,程序会跳转到相匹配的case (分支标号)处。分支标号只是说明switch内部位置的路标,在执行完分支中的最后一条语句后 ,如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或者switch结束为止。
#include<stdio.h>
int main()
{
int type;
scanf("%d",&type);
switch(type){
case 1:
printf("你好!");
break;
case 2:
printf("早上好!");
break;
case 3:
printf("晚上好!");
break;
case 4:
printf("再见!");
break;
default:
printf("啊,什么啊!") ;
}
return 0;
}
while循环与do-while循环的区别
while循环先判断执行条件,也就是while后面括号中的条件,如果为1,则会执行循环体;do-while循环则先执行循环体,然后再进行判断是否循环条件。
很明显,上面所示的流程图中,左边的是do-while,右边的是while,因为左边的先进循环体,右边先判断条件。
for循环
关于for循环,比较重要的地方就是循环次数问题了,for(i=0;i<n;i++)中,循环次数是n,循环结束以后,i的值是n。循环变量的控制,是选择从0开始还是从1开始,判断i<n还是i<=n,对循环的次数和循环变量的值都有影响。
#include<stdio.h>
int main()
{
int i;
for(i=0;i<5;i++)
{
printf("i=%d ",i);
}
printf("最后的i=%d",i);
return 0;
}
修改循环条件中i 的值,可以看到仍然有5次循环,但是最后输出的结果不一样。
for(i=1;i<=5;i++)
while循环和for循环是可以相互改造的。因此它们的流程图也是相同的。
break和continue语句
- break跳出循环;
- continue跳过循环(即跳过循环,执行下一次循环);
- break从嵌套循环中跳出时,break只能跳出其所在的循环。
第三章介绍
数据类型
- 整数:char,short,int,long,long long
- 浮点数:float,double,long double
- 逻辑:bool
- 指针
- 自定义类型
所表达的数的范围:char<short<int<float<double
Sizeof可以做什么
- 在C语言中,sizeof是一个运算符,给出某个类型或变量在内存中所占的字节数。
- sizeof是静态运算符,他的结果在编译的时刻就决定好了
- char: 1字节(8比特)
- short: 2字节
- int: 取决于编译器(CPU) 通常的意义是“1个字’
- long:取决于编译器(CPU) 通常的意义是“1个字”
- long long: 8字节
整数的范围
- char:1字节:-128~127
- short:2字节:-32768~32767
- int: 取决于编译器(CPU) 通常的意义是“1个字’:~
整数的输入输出
整数只有两种形式:int (%d)或 long long(%ld)
八进制和十六进制
- 一个以0开始的数字字面量是八进制
- 一个以0x开始的数字字面量是十六进制
- %o用于8进制,%x用于16进制
- 8进制和16进制只是如何把数字表达为字符串,与内部如何表达数字无关
浮点类型
在靠近0出有一小部分是浮点数无法表达的,即大约在0~左右。±inf表示正负无穷大,nan表示不是一个有效的数字。
字符类型
char是一 种整数,也是一种特殊的类型:字符。这是因为:
- 用单引号表示的字符字面量:'a','1'
- ''也是一个字符
- printf和scanf里用%c来输入输出字符
如何输入'1'这个字符给char c?
很明显,这样是不行的,因为当成一个字符的话明显“1”的ASCII码,并不是数字1。
字符计算
类型转换
自动类型转换:当运算符的两边出现不一致的类型时,会自动转换成较大的类型。(大的意思是能表达更大范围的数)
- char--->short--->int--->long--->long long
- int--->float--->double
- 对于printf,任何小于int的类型会被转换成int;float会被转换成double
- 但是scanf不会,要输入short.需要%hd
强制类型转换:要把一个量强制转换成另一个量(通常是较小的类型),需要注意这时候的安全性,小的变量不总能表达大的量。
- 强制类型转换的优先级高于四则运算
布尔类型
ture和false,这个布尔类型是在c99之后确定下来的,要引入头文件#include<stdbool.h>
逻辑运算
- 逻辑运算是对逻辑量进行的运算,结果只有0或1
- 逻辑量是关系运算或逻辑运算的结果
优先级:
- !> && > ||
短路
- 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
- 对于&&,左边是false时就不做右边了
- 对于||,左边是true时就不做右边了
条件运算
- 条件运算符的优先级高于赋值运算符,但是低于其他运算符
条件运算和逗号运算
这里会有警告出现
目前逗号表达式的作用:(for循环中放多个计算)
基本ASCII码参照表:(此图片资源来源于网络)
第四章介绍
函数
- 函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值
- 函数名(参数值);
- ()起到了表示函数调用的重要作用,即使没有参数也需要()
- 如果有参数,则需要给出正确的数量和顺序
- 这些值会被按照顺序依次用来初始化函数中的参数
函数的返回值
无返回值的函数:
- void函数名(参数表)
- 不能使用带值的return
- 可以没有return
- 调用的时候不能做返回值的赋值
有返回值的函数:
- 如果函数有返回值,就必须使用带值的return
函数先后关系
函数要先声明再使用,因为C编译器是自上而下的顺序分析你的代码的。
- 函数头以分号“;”结尾,就构成了函数原型;
- 函数原型的目的是告诉编译器函数长什么样;
- 函数原型中声明可以不写参数的名字的;
参数传递
调用函数:
- 如果函数有参数,调用函数时必须传递给它数量、类型正确的值
- 可以传递给函数值是表达式的结果
#include<stdio.h>
int max(int,int);
int main()
{
int a,b,c;
a=5;
b=6;
c=max(10,12);
c=max(a,b);
c=max(c,23);
c=max(max(23,45),a);
c=max(23+45,b);
printf("c=%d\n",c);
return 0;
}
int max(int a,int b)
{
if(a>b)
{
return a;
}
else
return b;
//return a>b?a:b;
}
类型不匹配:
- 调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞
- 编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的
- 后续的语言,C+ +/Java在这方面很严格
值传递:
- 每个函数都有自己的变量空间,参数也位于这个独立空间中,和其他函数没有关系
- 过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫做“实际参数”
- 现在为了更好的区分,我们将这些称呼为参数和值
局部变量
有时候叫本地变量,
- 函数的每次运行,就产生了一一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
- 定义在函数内部的变量就是本地变量
- 参数也是本地变量
局部变量的规则
局部变量是定义在块内的
- 他可以是定义在函数的块内,
- 也可以定义在语句的块内,
- 甚至可以随便拉一对大括号来定义变量
程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了
块外面定义的变量在里面仍然有效
块里面定义了和外面同名的变量则掩盖了外面的(局部变量优先原则)
不能在一个块内定义同名的变量
局部变量不会被默认初始化
参数在进入函数的时候被初始化了
注意事项
一定要在函数里面写上参数,不然会出现一些错误。
调用函数时的逗号和逗号运算符的区别
调用函数时的圆括号里的逗号是标点符号,不是运算符。
- f(a,b)这里的逗号是标点符号,不是运算符
- f((a,b))这里的逗号是运算符
- f(a,b)和f((a,b))的区别就是前者传入了两个参数,而后者只传入了一个参数
函数嵌套问题
C语言不允许函数的嵌套定义,可以函数里面可以放函数的声明,但不能放函数的定义。
第五章介绍
定义数组
<类型> 变量名称 [元素数量]
- int grades[100];
- double weight[20];
- 元素数量必须是整数
数组特点
- 数组是一种容器(放东西的东西)
- 数组中的元素具有相同的数据类型
- 一旦创建,不能改变大小
- 数组中的元素在内存中是连续依次排列的
数组的单元
- 数组的每个单元就是数组类型的一个变量
- 使用数组时放在[ ]中的数字叫做下标或索引,下标从0开始计数:
数组下标的有效范围:
编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃
写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束
#include<stdio.h>
int main()
{
const int number = 10;
int x;
int i;
int count[number];
for(i=0;i<number;i++)
{
count[i]=0;//初始化数组
}
scanf("%d",&x);
while(x!=-1)
{
if(x>=0&&x<=9)
{
count[x]++;
}
scanf("%d",&x);
}
for(i=0;i<number;i++)
{
printf("%d:%d\n",i,count[i]);
}
return 0;
}
数组的集成初始化
int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};
因此上面在初始化count数组时也可以不用写循环,直接使用数组集成初始化即可。
const int number = 10;
int x;
int i;
int count[number]={0}
//for(i=0;i<number;i++)
//{
// count[i]=0;//初始化数组
//}
数组的大小
sizeof给出整个数组所占据的内容的大小,单位是字节
sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
sizeof(a)/sizeof(a[0])得到的就是数组大小,这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码。
数组的赋值
数组变量本身不能被赋值,要把一个数组的所有元素交给另一个数组,必须采用遍历。(唯一方法,没有其他方法将一个数组赋给另一个数组了)
关于for循环中i的问题,一般情况下i是从0开始的,然后小于数组长度,这样恰好能从第一个数组元素开始进行遍历。
数组作为函数的参数时:
- 不能在[]中给出数组的大小
- 不能再利用sizeof来计算数组的元素个数!---->此部分会在后面数组中解释
二维数组的初始化
int a[][5]={
{0,1,2,3,4},
{2,3,4,5,6},
};
- 列数是必须给出的,行数可以由编译器来数
- 每行一个{},逗号分隔
- 最后的逗号可以存在,有古老的传统
- 如果省略,表示补0
- 也可以使用定位(only c99)
第六章介绍
取地址运算
这说明int在内存中占了4个字节,一个字节等于8个比特,所以一共是32个比特。
运算符&:
scanf("%d",&i);里面的&符号:
- &作用是获取变量的地址,他的操作数必须是变量
- int i;printf("%x",&i);
输出结果如下:(结果一样,这是在32位编译环境下)
换成64位编译环境就会输出结果不一样:
- 变量的地址
- 相邻的变量的地址
- &的结果的sizeof
- 数组的地址
- 数组单元的地址
- 相邻的数组单元的地址
指针
指针就是保存地址的变量,
指针变量:
- 指针变量的值是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
作为参数的指针:
访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量
- 可以做右值也可以做左值
- int k = *p
- *p = k+1
- 因此*p作为一个整体可以看做一个整数
指针的使用
#include<stdio.h>
void swap(int *pa,int *pb);
int main()
{
int a=5;
int b=6;
swap(&a,&b);
printf("a=%d,b=%d\n",a,b);
return 0;
}
void swap(int *pa,int *pb)
{
int t=*pa;
*pa=*pb;
*pb=t;
}
此代码就是使用了指针进行的两个数的交换,就是交换两个变量值
#include<stdio.h>
void minmax(int a[],int len,int *max,int *min);
int main(){
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,47,24,55,89};
int min,max;
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min,max);
return 0;
}
void minmax(int a[],int len,int *min,int *max){
int i;
*min = *max = a[0];
for(i=1;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
此外,函数返回多值时,某些值就只能通过指针返回,传入的参数实际上是需要保存带回的结果的变量。
指针常见错误
定义了指针变量,还未指向任何变量就直接开始使用。
指针与数组问题
为什么数组传进函数后的sizeof不对了,传入函数后的数组变成了什么?
- 函数参数表中的数组实际上是指针
- sizeof(a) == sizeof(int *)
- 但是可以用数组的运算符[ ]进行运算
数组参数
数组变量是特殊的指针,数组变量本身表达地址,所以:
- int a[10]; int * p = a; //无需使用取地址符&
- 但是数组的单元表达的是变量,需要使用&取地址
#include<stdio.h>
void minmax(int *a,int len,int *max,int *min);
int main(){
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,47,24,55,89};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));
printf("main a=%p\n",a);
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min,max);
int *p = a;
printf("*p=%d\n",*p);
printf("p[0]=%d\n",p[5]);
return 0;
}
void minmax(int *a,int len,int *min,int *max){
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
printf("minmax a=%p\n",a);
a[0]=1000;
*min = *max = a[0];
for(i=1;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
数组变量是特殊的指针
数组变量本身表达地址,所以
- int a[10];int *p=a;//无需使用&取地址
- 但是数组的单元表达的是变量,需要用&取地址
- a=&a[0]
[ ]运算符可以对数组做,也可以对指针做:
- p[0] <==> a[0]
*运算符可以对指针做,也可以对数组做:
- *a=1;
数组变量是const指针,所以不能被赋值
- int a[] <==> int * const a 不能再赋值
#include<stdio.h>
int main(){
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,47,24,55,89};
int *p = a;
printf("*p=%d\n",*p);
printf("p[0]=%d\n",p[0]);
printf("p[5]=%d\n",p[5]);
return 0;
}
对着个代码运行的结果可以这样理解:p指针指向的是数组a,p[0]是指将所指的地方当做一个数组,取该数组的第1个元素,p[5]表示取数组a的第6个元素。
指针运算
#include<stdio.h>
int main(){
char ac[] = {0,1,2,3,4,5,6,7,8,9};
char *p = ac;
printf("p =%p \n",p);
printf("p+1=%p \n",p+1);
int ai[] = {0,1,2,3,4,5,6,7,8,9};
int *q = ai;
printf("q =%p \n",q);
printf("q+1=%p \n",q+1);
return 0;
}
地址变化:sizeof(char)=1,sizeof(int)=4.当我们给一个指针+1的时候,我们的地址变化并不是在地址值上+1,而是在地址值上加一个sizeof(数据类型)。
这些算术运算可以对指针做:
- 给指针加、减一个整数(+, +=,-,-=)
- 递增递减(++/- -)
- 两个指针相减
两个指针相减的结果是什么?通过以下程序可以很清楚的了解到。
这里也并不是直接的通过地址值相减,而是减完后除以了sizeof(数据类型),得到的是这两个指针之间有多少个这样的地址,或者能放几个这样的地址。、
*p++
- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
- *p的优先级虽然高,但是没有++高
- 常用于数组类的连续空间操作
- 在某些CPU上,这可以直接被翻译成一 条汇编指令
指针类型
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接互相赋值的
- 这是为了避免用错指针
第七章介绍
动态内存分配
free()
字符串
字符数组不是字符串
char word[]={'H','e','l','l','o','!','\0'};
那么什么是字符串呢?
- 以0(整数0)结尾的一串字符
- 0和'\0'是一样的,但是和'0'不同
- 0标志字符串的结束,但它不是字符串的一部分,计算字符串长度的时候不包括这个0
- 字符串以数组的形式存在,以数组或指针的形式访问,更多的是以指针的形式
- string.h里面有很多处理字符串的函数
字符串变量
char *str = "hello";
char word[] = "hello";
char line[10]= "hello";
其中char *str = "hello";是指有一个叫str的指针,它指向了一个字符数组,这个数组的内容是hello;
char word[] = "hello";是指就是有个一叫做word的数组;
char line[10]= "hello";是指line这个数组长度为10,里面放的内容为hello(hello有五个字符,在line中占据6个位置)
字符串常量
char *s="hello world";
- s是一个指针,初始化为指向一个字符串常量
- 由于这个常量所在的地方,实际上s是 const char *s,但是由于历史原因,编译器接受不带const的写法
- 但是视图对s所指的字符进行写入会导致严重的后果
- 如果需要修改字符串,应该用数组:
- char s[ ]="Hello world!";
使用指针还是数组?
char * 是不是字符串
字符串的赋值
- char *t="title";
- char *s;
- s=t;
- 并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的
字符串的输入输出
- char string[];
- scanf("%s",string);
- printf("%s",string);
- scanf读入一个单词(到空格、Tab或回车为止)
输出结果:
安全的输入
- char string[8];
- scanf("%7s",string);
- 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小1
常见错误:
- char *string;
- scanf("%s",string);
- 以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了
- 由于没有对string初始化为0,所以不一定每次运行都出错(没有初始化,可能这个时候string指针指向的是某个内存中不能写入,程序就会崩溃)
空字符串
- char buffer[100]="";
- 这是一个空字符串,buffer[0]='\0'
- char buffer[]="";
- 这个数组的长度是1(这个buffer数组里头放不下任何字符串)
字符串数组
- char **a;
- a是一个指针,指向另一个指针,那个指针指向一个字符(串)
- char a[][];
字符的输入输出
putchar
- int putchar(int c);
- 向标准输出写一个字符
- 返回写了几个字符,EOF (-1) 表示写失败
getchar
- int getchar(void);
- 从标准输入读入一个字符
- 返回类型是int是为了返回EOF (-1)
- Windows-->Ctrl-Z
- Unix-->Ctrl-D
字符串函数strlen
string.h中的函数:
- strlen:返回s的字符串长度(不包括结尾的0)
可以看到strlen输出结果表示不带结尾0的字符串长度,而sizeof表示的是带0的占据的内存空间的字符个数
- strcmp:比较两个字符串
- 返回0:s1==s2
- 返回1:s1>s2
- 返回-1:s1<s2
- strcpy:
- char * strcpy(char *restrict dst,const char *restrict src);
- 把src的字符串拷贝到dst
- restrict表名src和dst不重叠
- 最后返回的是dst
- strchr:字符串中寻找字符
- 返回NULL表示没找到
- 1
第八章介绍
枚举
用符号而不是具体的数字来表示程序中的数字
- 枚举是一种用户定义的数据类型,它用关键字enum来声明:
- enum 枚举类型名字 {名字0, ....名字n};
- 枚举类型的名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是就是常量符号,它们的类型是int,值则依次从0到n。如:
- enum colors { red, yellow, green } ;
- 就创建了三个常量,red的值是0,yellow是1, 而green是2。
- 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字。
虽然枚举类型可以当做类型使用,但是实际上很少用(不好用)。
结构类型
- 和局部变量一样,在函数内部声明的结构类型只能在函数内部使用
- 所以通常在函数外部声明结构类型,这样就可以被多个函数使用了
声明结构的形式1:
struct point
{
int x;
int y;
};
struct point p1,p2;
p1,p2都是point,里面有x和y的值。
声明结构的形式2:
struct point
{
int x;
int y;
}p1,p2;
p1,p2都是point,里面有x和y的值。
声明结构的形式3:
struct
{
int x;
int y;
}p1,p2;
p1,p2是一种无名结构,里面有x和y的值。
结构的初始化
给的值填进去,没给的填0,类似于数组
#include <stdio.h>
struct Time
{
int hour1;
int min1;
int sec1;
};
//必须按照结构体顺序
struct Time t1={
17,
49,
54
};
//与结构体顺序无关
struct Time t2={
.min1=49,
.sec1=54,
.hour1=17
};
//与结构体顺序无关
struct Time t3={
min1:49,
sec1:54,
hour1:17
};
int main()
{
printf("t1:%d:%d:%d\n", t1.hour1, t1.min1, t1.sec1);
printf("t2:%d:%d:%d\n", t2.hour1, t2.min1, t2.sec1);
printf("t3:%d:%d:%d\n", t3.hour1, t3.min1, t3.sec1);
return 0;
}
//结果输出
t1:17:48:54
t2:17:48:54
t3:17:48:54
结构成员
- 结构和数组有点类似
- 数组用方括号[]和下标访问其成员,如a[0]=10;
- 结构用 . 运算符和名字访问其成员
- today.day
- student.firstName
- p1.x
- p2.y
结构运算
- 要访问整个结构,直接用结构变量的名字
- 对于整个结构,可以做赋值、取地址,也可以传递给函数参数
- p1=(struct point) {5,10}; //相当于p1.x=5,p1.y=10
- p1=p2; //相当于p1.x=p2.x,p1.y=p2.y
#include<stdio.h>
struct date
{
int month;
int day;
int year;
};
int main()
{
struct date today ;
today=(struct date){07,31,2014};
struct date day;
day=today;
printf("Today's date is %i-%i-%i.\n",today.year,today.month,today.day);
printf("Thismonth is %i-%i-%i.\n",day.year,day.month,day.day);
return 0;
}
结构指针
- 和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
- struct date *pDate = &today;(不用&符号取地址编译错误)
结构作为函数参数
- int numberOfDays(struct date d)
指向结构的指针
-
struct date { int month; int day; int year; }myday; struct date *p = &myday; (*p).month = 12 ; p->month=12;
-
表示指针所指的结构变量中的成员
结构数组
struct date dates[100];
struct date dates[]={
{4,5,2005},{2,4,2006}
};
自定义数据类型Typedef
全局变量
- 定义在函数外面的变量是全局变量
- 全局变量具有全局的生存期和周期性
- 它们与任何函数都无关
- 在任何函数内部都可以使用它们
- 没做初始化的全局变量会得到0值
- 指针会得到NULL值
- 只能用编译时刻已知的值来初始化全局变量
- 它们的初始化发生在main函数之前
静态局部(本地)变量
- 在本地变量定义时加.上static修饰符就成为静态本地变量
- 当函数离开的时候,静态本地变量会继续存在并保持其值
- 静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值
加上static使之称为静态局部变量之后每次进入就不会重新初始化了
- 静态本地变量实际上是特殊的全局变量
- 它们位于相同的内存区域
- 静态本地变量具有全局的生存期,函数内的局部作用域
- static在这里的意思是局部作用域(本地可访问)