C语言学习一些总结

惭愧,现在还在学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所指向的内存区,该函数无返回值

待续。。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值