惭愧,现在还在学C语言,都怪自己以前不学好。
1.一维数组定义与初始化():
int array[5];//声明一个数组,
int array[5] = {1,2,3,4,5};//声明的时候进行初始化,没有初始化默认为0,假如是字符型数组,默认为 ' \0 ' ,
int array[ ] = {1,2,3,4,5};//省略数组长度,默认长度为数组元素个数。
2.二维数组的定义与初始化:
int array[ 3] [ 4 ];//声明一个二维数组,
int array [ 3 ] [ 4 ] = {{1,2,3,1},{4,5,6,4},{7,8,9,7}};//声明的时候进行初始化,没有初始化的默认为0,假如是字符数组,默认为' \0 ',
int array[3][4] = {1,2,3,1,1,4,4,7,5,6,7,8,9};//另一种形式初始化,
int array[3][4] = {{1,2},{0,0,3},{7}};//部分元素初始化,其余为0,
int array[][4] ={{1,2},{0,0,3},{7}};//省略第一维的长度。
3.字符数组的定义与初始化:
char string[5] = {'a ','b ','b ','g ','h '};//定义一个字符数组初始化,其他省略长度,默认初始为' \0 '的特性类比int数组,
char string[5] = {"abbgh"};//以字符串初始化,也可以省略花括号。
4.字符数组的输入与输出:
scanf("%s",string);//输入一个字符串,空格被当作字符串间默认的分隔符,。如下:
char str1[5],str2[5],str3[5];//假设输入how are you?
scanf("%s%s%s",str1,str2,str3);//输入后结果如下:
str1 = "how";
str2 = "are";
str3 = "you?";//未被赋值的默认为' \0 ',
printf("%s",string);//不能用printf("%c",string[1]);因为%s需要的是一个数组名
scanf("%s",string);//输入一个字符串,有局限,不能包含空格,
puts(string);//输出字符串,将' \0 '转换成' \n '输出,
gets(string);//输入字符串,将' \n '转换成' \0 ' 输入,和puts一样一次只能输入一个字符串。
5.数组名作函数参数:
实参和形参应分别定义数组:
void function(int a[ ]);
int a[10];
function[ a ];
且类型必须一致
多维数组名作函数参数:与一维基本类似,但是形参定义中第一维长度不可省:
void funtion(int [ ] [ 10 ]);
6.变量的指针,指针变量:
定义一个变量a: int a = 8;
含义:假设给该变量的内存地址为0065FDF4H,则系统在0065FDF4H的存储单元内将 8 存放进去,0065FDF4H就是这个变量的指针;
定义一个指针变量p : int *p = & a;
含义:将变量a的指针也就是存储地址赋给变量p,p内存放的是变量的地址而不是像8一样的数值,同时当然系统会为p也分配一个类似0065FDF4H的地址,作为存放为0065FDF4H的地址,比如可以是为0068DDBC(假设)。
7.指针变量作函数参数:
参数传递形式:值传递。指针呢?当然还是值传递,只不过这里传的值是地址,先来看普通的值传递的处理过程:
int max(int a,int b)
{
return a > b? a : b;
}
主函数调用:
int a = 8 ;//假设a 的存储地址为:ox001
int b = 6 ;//假设a 的存储地址为:ox002
int c = max(a,b);
处理过程:max函数接受主函数传递过来的实参的值,同时为两个形参a,b分别分配一块内存空间,地址假设为ox003,ox004,
并将接受过来的值分别存放至ox003,ox004里面(实参是存放在ox001,ox002里面),然后返回结果。
再来看指针做参数传递:
int max(int *a,int *b)
{
return *a > *b? *a : *b;
}
主函数调用:
int a = 8 ;//假设a 的存储地址为:ox001
int b = 6 ;//假设a 的存储地址为:ox002
int c = max(a,b);
处理过程:max函数接受主函数传递过来的实参的值,同时为两个形参a,b分别分配一块内存空间,地址假设为ox003,ox004,
并将接受过来的值分别存放至ox003,ox004里面(实参是存放在ox001,ox002里面),但是这里存放的值不再是简单的数值,而是地址值,
后面通过*a,*b 的操作得到其实就像使用直接使用实参a,b一样的效果,也就是说*a = a(实参里的a),*b = b(实参里的b),所以这里是直接对实参的值
进行操作,而不是像上面的那个例子一样对副本进行操作。然后返回结果。
8.printf 和 scanf:
int a;
scanf("%d",&a);//&求地址操作符,&a取得为变量a分配的存储地址,这句含义,将输入的值存放至&a所指的地址
printf("%d",a);//a直接取得变量的值,输出,在这里同样需要地址,就int b = a;这个操作我们是从&a地址里取值赋给&b所指存储单元,但是没有用int &b = &a;来表示。
特殊情况:字符数组的输入和输出:
char a[15] = "I love you!";
scanf("%s",a);//不能用&a,因为a里面存得就是地址,
printf("%s",a);//不能用*a,我想这里是系统内部进行了处理,应该是这样:
for(int i=0;*(a+i) != ' \0 ';i++)
{
printf("%c",*(a+i));
}//may be 阿,我也没有深究
9.指针与数组:(数组名为数组首元素的地址!)
一维数组:int a[10];//系统分配10个连续2字节的存储单元,并将第一个存储单元的分配地址赋给a。
定义指针:int p = a;//使p指向数组a的首地址,现在通过p的移动就可以随机存取数组的内容了,但是a不能移动,因为a是地址,已经分配好了的
关系:a = p; p = & a[0]; *(a + i) = * (p +i) = a[i]。
二维数组:int a[10][10];//其实就是一个一维数组,只不过这个一维数组的元素又是一维数组,也就是说现在a应该指向这个特殊一维数组的首地址也就是&a[0],
所以a = &a[0]。其实就是第0行的开始地址,那么a+i就是第i行的开始地址。
定义指针:int p = a;//类似一维数组,p指向数组的第0行的开始地址,同样有 p + i = a + i = &a[i], *(p +i) = *(a + i) = a[i];//注意这里a[i]还是地址,因为它也是一个一维数组名。
关系举例:
0行的地址:&a[0],(a+0);
0行0列元素的地址:a[0],*(a+0);
0行1列元素的地址:a[0] + 1, *(a+0) + 1;
1行的地址:&a[1],(a+1);
1行0列元素的地址:a[1],*(a+1);
1行1列元素的地址:a[1] + 1, *(a+1) + 1;
1行1列元素的值:a[1][0],*(a[1] + 1 ),*(*(a+1) + 1);
总结:指针与数组是可以相互对应的,但是指向数组的指针必须用已经定义好的数组名来初始化。
10.指针与数组在参数传递中的对应关系:假设有函数 void fun(形参列表);
形实参都用数组名://前已介绍
形实参都用指针数组:
int a[10];
int *p = a;
fun(p);
fun定义:
void fun(int *p){ }
形参数组,实参指针:
int a[10];
int *p = a;
fun(p);
fun定义:
void fun(int p [ ] ){ }
形参指针,实参数组:
int a[10];
fun(a);
fun定义:
void fun(int *p){ }
11.指针与字符串:
前面有字符数组定义与初始化可以为 char string[] = "I love you";
实际上就是把字符串"I love you"的首地址赋给数组名string。
现在用指针指向字符串:
char * string = "I love you"。
12.字符数组与字符串指针的比较:
赋值方式:对字符数组不能用char str[14];str="i love you" ; 赋值,而字符指针可以 char * str;str = "i love you",
初始化:char str[14] = "i love you " 不能等价于 char str[14];str = " i love you ",
而 char * str = "i love you" 等价于 char * str;str="i love you"。
13.指针数组,数组指针:
指针数组:int *p[4]; // 定义一个四个元素的数组p,p为数组名,指向首元素地址,数组元素类型为指针,可以指向int类型的变量;
数组指针:int (*p)[4];//定义一个int 类型的指针,它可以指向一个包含四个元素的一维数组,这里其实就是二维数组,p可以指向一个二维数组的首行的起始地址:
int a[4][3];p=a;//用p就可以引用这个二维数组了,如*(*(p+i)+j)取得i行j列的元素值,i,j从0开始。
14.函数指针,指针函数:
函数指针:函数名就是函数调用的开始地址,将它赋给一个指针变量,就可以利用该指针变量进行调用函数。
跟函数一样,需要先声明:
int max(int i,int j);
int (*p)(int i ,int j);
p = max;
int a=8,b=6;
int c = (*p)(a,b);//这就是面向对象里面的代理和委托阿。
函数指针:函数调用后返回一个指针,需指定指针类型。
int * a(int x,int y);
15.main函数的形参:
void main(int argv, char *args[]);
argv 是命令行参数的个数,*args[]存储各个命令行参数,这是一个一维字符指针数组,里面每个元素都是字符串指针,指向每个字符串参数
获得第i个命令行参数的地址:*(args+i) = args[i].
16.结构体与共用体:
结构体的定义:结构体属于自定义数据类型,定义了一个结构体可以像使用int ,char等基本数据类型一样,当类型来使用
struct student {
char *name;
int no;
};//structt student 就是一个类似int 的类型名,要用它定义变量的时候,就可以这样用 struct student student1,student2;
同时也可以在声明的时候定义变量,比如:
struct student {
char *name;
int no;
}student1,student2;
也可以省略结构体名,也就是student,但是这样做的局限就是,后面不能再用这个结构体类型声明变量了,只能在定义的时候声明,所以一般不这么做。结构体成员的引用:
对一下结构体:
struct date {
int year;
int month;
int day;
}
struct student {
int no;
char *name;
struct date birthday;
}student1;
引用成员的方法为:
student1.no student1.namestudent1.date.yearstudent1.date.monthstudent1.date.day
假如用指针指向了结构体,如:struct student *p=&student1;//&student1为结构体的首地址,一般是第一个成员的地址
则可以这样引用:
(*p).no (*p).name(*p).date.year(*p).date.month(*p).date.day
或
p->no p->namep->date.yearp->date.monthp->date.day
共用体://形式与结构体基本类似
定义形式:
union data {
int i;
char ch;
float f;
}a,b,c;
或union data a,b,c;
也可以省略:
union {
int i;
char ch;
float f;
}a,b,c;
注意:结构体所占内存长度为各个成员内存长度之和,共用体所占内存长度为最大长度的成员长度。
再注意:
共用体不能初始化;
共用体是共享内存空间,一次只能使用一种成员类型;
共用体中起作用的成员是最后一次存放的成员,以前存入的成员会无效;
共用体变量的地址和所有成员地址一样;
17.枚举变量:
定义:enum weekday {sun,mon,tue,wed,thu,fri,sat} weekday1,weeday2;//和前面类似,依顺序各项的值默认为0,1,2,3,4,5,6,也可以自己指定值:
enum weekday {sun=7,mon=1,tue,wed,thu,fri,sat};.//后面没有指定的顺序加1。
使用:weekday1 = sun;//weekday1的值为7,printf("%d",weekday1);//输出7.
赋值:weekday1 = (enum weekday)2;//将2强制转换赋给weeday1.等效于weekday1 = tue;
18.自定以类型名:typedef
举例:
typedef char ElemType;
typedef struct student {
int no;
} ElemType;
typedef union data {
int i;
}ElemType;
将基本数据类型命名为自定义的类型名。以后就可以直接用该类型名声明变量:ElemType * p;。。。
19.文件操作:
文件指针的定义:File *fp;
打开文件:fp = fopen("file.txt","r");//后面的r是文件打开模式,有多种,暂不介绍,依据文件打开的模式,文件指针会被定位至相应的位置
关闭文件:fclose(fp);
举例;
if((fp=fopen("file.txt","a"))==NULL) {
printf("can not open the file");
}
文件读写函数:
fputc&fgetc:读写单个字符
fputc(ch,fp);//写一个字符到fp指定的文件,如果写入成功,则返回写入的字符,否则返回EOF,也就是-1,
fgetc(fp);//从文件指针指向的位置读一个字符,如果已到文件末尾,则返回EOF。
fread&fwrite:读写一组数据,比如数组,结构等等
fread(buffer,size,count,fp);//从文件读取count个size大小的数据到bufffer指定的起始地址去;
fwrite(buffer,size,count,fp);//将buffer起始地址处的size大小的数组写count个到文件fp中去。
举例:
struct student stu;
fread(&stu,sizeof(struct student),1,fp);
fwrite(&stu,sizeof(struct student),1,fp);
如果读写成功则返回count 的值
fprintf&fscanf://对文件的格式化读写
举例:
i=3,j=4.5
fprintf(fp,"%d,%6.2f",i,j);
则输入到文件中为:3, 4.5
设磁盘上有字符:3,4.5
fscanf(fp,"%d,%f",&i,&j);
读取成功后 i=3,j=4.5
fgets&fputs://整行读写
fgets(str,n,fp);//从fp读n-1个字符到str地址,最后一个字符加上' \0 ',总共n个字符,当读完n-1个字前遇到换行符或EOF则结束读取,该函数返回str的首地址
fputs(str,fp);//向fp中把str指向的数据写一行的文件中去,末尾的' \0 '不输出,被转作换行符,写入成功返回0,否则返回EOF。
文件定位:
rewind(fp);//重新定位于文件开始
feof(fp);//判断文件指针是否到达文件末尾,到达则为true。
fseek(fp,位移量,起始点);//随机读写函数
位移量指定文件指针移动的长度,可以为负
起始点指定文件指针的起始位置:
文件开始:SEEK_SET 或 0
文件当前位置:SEEK_CUR 或 1
文件末尾:SEEK_END 或 2
举例:
fseek(fp,100L,0);//将fp移动到离文件头的100个字节处
fseek(fp.50L,1);//将fp移动到离当前位置50个字节处
fseek(fp,-100L,2);//将fp从文件末尾往前移动100个字节
ftell函数:得到流式文件读取的当前位置。
int i = ftell(fp);//i=-1表示调用出错
错误检测:
ferror(fp);//检测在对文件进行输入输出操作后fp是否发生错误,若有错误则返回真,并且每一次检测都会返回一个新的值,会覆盖之前可能有的错误信息
所有应该在每一次调用输入输出函数后进行检测
clearerr(fp)清楚ferror保留的错误信息,置ferror(fp)=0。
补充:动态分配内存函数:
malloc函数:
void * malloc(unsigned int size);
在内存的动态存储区分配一个长度为size的连续空间,并返回该空间的首地址,否则返回NULL,该指针类型void,引用的时候需要强制转换
struct student *p = (struct student *)malloc(sizeof(struct student));
calloc函数:
void * calloc(unsigned n,unsigned size)
在内存的动态存储区分配n个长度为size的连续空间,并返回该空间的首地址,否则返回NULL,该指针类型void,引用的时候需要强制转换
int *p = (int *)calloc(10,2);//现在p可以看做是一个一维数组的首地址
free函数:
void free(void *p);//释放指针p所指向的内存区,该函数无返回值
待续。。。。。