C语言的零碎知识点


若是晴日的午后,阳光透过院子照进宅子,在磨白的梳妆台上留下斑驳的印记。
上帝借由各种途径让让我们变得孤独,好让我们走向自己
我有那么多奇妙能力,却没有你


基本数据类型及关系运算

  • 注释不能嵌套注释,/…//…/ 是不合法的
  • C语言中要求 声明部分在前,执行部分在后,以下代码是不合法的
int a;
a=10;
int b;   //错误,因为变量定义int放在了执行部分,应该放在a=10;前面
b=a+20;
  • 八进制以0开头,转义字符是\ddd
  • 十六进制以0x开头,转义字符是\xhh
  • 若ab均为int则a/b也为int
  • '1’和1是不同的,前者是char,后者是int。'1’在与int计算的时候用其ascii
  • ‘1’=49;‘A’=65;‘a’=97;
  • 算术运算符中,+,-,*,/ 可以用于整型数据或实型数据,%只能用于整数
  • C语言的指数由十进制数、阶码标志“e”或“E”和阶码组成,阶码只能是整数。e和E前面必须有数字,后面必须是整数
    就是 a e b,a可以是浮点数,b只能是整数,e左右都必须得有数
  • &&,||,在计算时,若左侧已经可以判断就不会执行右侧,如a&&b,如果a为false则不执行b
  • printf()和scanf()都是有返回值的

printf()返回的是它输出的内容的长度
如 int n=printf(“abc”); 则控制台输出abc,n的值为3
scanf()返回的是它接收的数据的个数
如 int n=scanf(“%d%d”,&a,&b);则在输入值之后,n的值为2

  • 数据格式控制字符

%d:有符号,整形
%u:无符号,整形
%f:float
%c:字符型,char
%s:字符串

  • 对于输入输出的格式控制字符

printf():
%4d表示输出至少占4格,不够(数位<4)则补空格,够(>=4)则输出实际数位
%5.3d表示至少要输出3位数,当数据长度小于3时左边补0
%5.3f表示输出3位小数
%.3s表示指数从字符串的前3个字符
scanf():
%4d表示输入至多占4格,如果输入的是5为数,则从高到低取4位。如,如果输入的123567,则实际上内存中的数据为1235

  • printf()中,如果需要输出%,必须在格式字符串中用%%代替单个%
    %%与转义字符无关,它与printf如何处理格式说明符有关,转义字符对所有字符串有效,并在编译时完成,格式说明符(%)仅在一些函数使用,并在运行时使用。简而言之,在字符串或单个字符中,%都能代表它本身,不需要也不能用%%来表示一个%,只在像printf这样使用%作为格式说明符的函数中规定使用%%来明确表示一个%字符

  • puts()函数遇到’\0’就停止输出,如果字符数组中存储有’\0’,则即使后面还有字符也不会输出

  • getchar()的返回值是所键入字符的ASCII

  • putchar()的返回值是所返回字符的ASCII,出错则返回EOF(-1)

  • 逗号表达式的优先级最低,从前往后执行,最后一个表达式的值就是整个逗号表达式的值
    如return (a,b);则返回b

  • sizeof() 获取变量和数据类型所占内存大小(字节数)
    char c[]=“abc” sizeof©的值为4

  • 优先级:强制类型转换>算术运算符>赋值运算符
    float(1/2)的结果为0,因为先算括号里面的

  • #define定义的值就直接将其替换到表达式的位置,不会先计算

#define N 4+4
printf("%d",3*N*2+1);
输出21,因为计算的时候是3*4+4*2+1
  • 只有整形可以取余,浮点型不可以
  • i++是先用再加,++i是先加再用

选择结构与循环

  • for或者while循环如果不设置循环的判断条件就是永远为真
    如while(),for(i=0;;i++)
  • 注意有时循环条件被跳过了也会造成死循环
    for(i=1;i!=10;i++) i每次回增加2,只会产生奇数,不会达到10
  • ‘\0’等价于0,在循环中为假
  • =是赋值符号,==才是判断
    如果for循环中是for(i=0;i=10;i++)则每次执行循环,就会把10赋给i,赋值成功i为真,会一直执行循环
  • switch 后面的表达式一般是int或者char不能用double
  • case后面的常量表达式类型应该与switch一直,switch后面是int就是int,是char就是char,才会执行case中语句
  • 如果switch的结果是’1’,而case中的是1,一个是int一个是char,则不会执行case的语句
  • 如果switch的结果是49,而case中的是’1’,选择到的结果是其ascii则会执行
  • case后面的常量表达式仅起语句标号作用,并不进行条件判断
  • case语句后如果没有加break,就会继续执行下一个case,遇到break则跳出switch

数组与函数

  • 一维数组的初始化: 数据类型符 数组变量名[ 常量表达式 ]={表达式1,表达式2,表达式3}

  • 定义数组时,如果没有为数组变量赋初值,那么就不能省略数组的大小。而且数组不初始化,其数组元素的值为随机值

  • 除了在定义数组变量时用初值列表对数组整体赋值以外,无法再对数组变量进行整体赋值

  • 一维数组与二维数组在初始化时如果没有赋初值则自动补0

  • 二维数组在内存中是以一列形式存储的

  • 二维数组的初始化:数据类型 数组变量名[ 行常量表达式 ] [ 列常量表达式 ]={{第0行初值表},{第1行初值表},…,{最后一行初值表}}

  • 二维数组的初始化:
    1.将初值放在一对{}中,与一维相同:int a[2][3]={1,2,3,4,5,6};
    2.按行初始化,每一行初值均采用一对{ }括起来:int a[2][3]={{1,2,3},{4,5,6}};
    3.定义二维数组的同时给数组初始化,则可以省略行数,但列数不能省略

  • 字符数组的定义:

char str[] ="china";
char str[]={'c','h','i','n','a','\0'};  //这样定义的话必须以'\0'结尾
char str[]={"china"};
  • 定义时,如果指定了数组的大小,则未指定的单元值为0

  • 字符串常量只能在定义字符数组时赋初值给字符数组变量

  • 在数字符数组的长度的时候,末尾的‘\0’也要被计算上

  • 字符串的输入: gets(字符数组变量名) 遇回车返回,空格不返回

  • 字符串的输出: puts(字符串地址) 遇到’\0’结束输出,'\0’变成换行

  • 字符串的长度: strlen(字符串地址) 从给定地址开始到第一个’\0’结束,‘\0’ 不包含在长度中

  • 字符串的复制: strcpy(字符数组1,字符数组2) 或者 strncpy(字符数组1,字符串2,长度n) 把2(前n个字符)复制给1

  • 字符串的比较: strcmp(字符串1,字符串2) 逐个比较ASCII,1>2则返回正整数,1<2则返回负整数,1=2返回0

  • 字符串数组: 构成数组的数据是字符串

  • 字符串数组的初始化:char 字符串数组名[ 行数m ] [ 列数n ]={字符串1,字符串2,…,字符串m}
    每个字符串的长度应小于过等于n-1,因为字符串的结尾符’\0’占用一个单元

char city[] [10] ={“beijing”,“Shanghai”,“wuhan”};
city[0] --------> ‘b’ ‘e’ ‘i’ ‘j’ ‘i’ ‘n’ ‘g’ ‘\0’ 0
city[1] --------> ‘s’ ‘h’ ‘a’ ‘n’ ‘g’ ‘h’ ‘a’ ‘i’ ‘\0’
city[2] ---------> ‘w’ ‘u’ ‘h’ ‘a’ ‘n’ ‘\0’ 0 0 0
多余空位补0

  • 定义数组时,如果为数组变量赋了初值,可以省略数组的大小,但是如果没有赋初值,则不能省略数组的大小
    初值至少要有一个值

  • 数组名仅仅是数组在内存中的地址,本身不代表数组单元的全部

  • 数组名是地址不可以被赋值也不可以++或–

  • 数组作为函数参数和返回值的时候,都用的是数组名

  • 数组名做实参时,它和对应形参直接是地址传递

  • 向字符数组赋值的时候:
    c[10]={“1”,“1”} //像普通数组这样赋值
    *c=“book”; //用指针赋值

  • strlen() 求的是字符串的长度,字符串长度到\0就结束了,即使字符数组后面还有元素,也不会被计算(此处联系一下put()函数)

  • 如果在定义函数时省略返回值类型符,则默认为int型

  • 值传递:函数调用时,为形参分配内存单元,并将实参的值复制到形参中,调用结束后,形参所占的内存单元被释放,实参的内存单元保留并维持原值。(单向传递)

  • 地址传递:在函数调用时,实参数据的存储地址作为参数传递给形参。此时,形参与实参占用同样的内存单元,函数中对形参值的改变也会改变实参(双向数据传递)

  • 地址传递时,实参或形参必须是地址常量或地址变量(形参和实参指向同一个内存单元)

  • 函数名和函数调用不同,函数名是一个地址,不能传参数,函数调用可以传参数,函数的返回值给的是函数调用而不是函数名

int max(int x,int y){//省略}
main()
{//函数名是max
max(1,2);//这是一个函数调用,返回值给的是max()而不是函数名max
}
  • 在函数中对形参结构体进行改变不影响实参的结构体中的数据
  • 定义在复合语句中的变量作用域只在复合语句范围内
  • 静态变量的值在实义赋值之后是不会改变的
  • 静态变量若在定义时为赋初值,则系统自动赋初值0,若定义时赋了初值,则赋初值操作在程序开始执行时就执行了
  • 虽然离开了定义静态变量的函数后不能使用了,但是如果再次调用定义它的函数时,它又可以继续使用,且保存了之前被调用后留下的值。
  • 全局变量的作用域是整个文件,在函数中修改其值会使得值发生变化,当全局变量与同名局部变量冲突时,在该函数中以局部变量优先
  • 函数的返回值是定义时已经确定了的,int就返回整数,char就返回字符
    如下代码返回值为一个整数
int fun(int *p)
	return *p;
  • C语言中不允许函数嵌套定义,但是允许嵌套调用

指针

  • 指针实际上就是内存地址,指针变量就是专门用于存储其他变量地址的变量
  • 当指针作为函数的参数的时候,在函数体中对指针进行修改会改变指针指向的数的值
    如void(*p)
  • 优先级中 -> () []的优先级最高
    ++p->x,先执行p->x,再执行++;也就使得内存中p->x的值加1
    (++p)->x,先执行++p,再执行->x;也就是先移动指针,再取值
    ++(*p->y),先执行p->y,再执行 *,最后执行++
    ++和–具有右结合性

*号:
1.双目运算符就是乘法
2.前有类型,后有分号说明是类型定义,*是一个标志
3.指针运算:地址变量前有*说明为内容值,不是读就是写,放在赋值号左边是写,其它情况都是读

  • 内容变量得内容。地址变量得地址,得谁地址指向谁
    内容变量画房子,指针变画指向
  • 有*为内容值
  • 无*为地址,地址的赋值意味着改指向
  • *与&互逆,*与[]等价
  • &只能用于变量,不能用于表达式,如&(a+1)是错误的
  • 如果等号右边的指针类型与左边的指针类型不同,则需要强制类型转换
int a;
int *pi=&a;
char *pc;
pi=(char *)pi;//强制类型转换
  • 零指针(空指针):int *p=NULL; 或者 int *p=0;

指向数组的指针变量

一维数组的指针与指针变量
  • 数组名代表数组的首地址,而且是一个地址常量
    数组名是 1.首地址 2.地址常量 3.就是a[0]的地址
  • 当指针变量指向数组中的某一个元素时,指针变量加1后指向数组下一个元素,减1指向数组中前一个元素
  • 一维数组实际上是个列指针(联系多维数组)
  • 地址三等价,元素四等价

p+i 与 a+i 与 &a[i] 等价

*(p+i) 与 *(a+i) 与 a[i] 与 p[i] 等价

p++ ; ++p ; p+=1 ; p=p+1
p-- ; --p ;p-=1 ; p=p-1

*p++ *(p++) 先取出地址值再移动指针
*++p *(++p) 先移动指针在取出值
(*p)++ ++(*p) ++*p p所指的内容值加1
(*p)-- --(*p) --*p p所指的内容值减1

  • 举个栗子
int b[5]={10,30,20,15,40};
int *q-b+2

初始:q指向20(如下操作独立,不是顺序执行)
*q++:表达式值为20,q指向b[3]
*++q: 表达式值为15,q指向b[3]
(*q)++:表达式值为20,q指向b[2](先输出再加)
++(*q):表达式值为21,q指向b[2](先加再输出)

  • 形参中的数组实际上是一个指针变量,并不是真正的数组
  • 若形参是数组或指针变量,则在函数中可以通过改形参改变实参的值
多维数组的指针与指针变量
  • 假设a是一个二维数组,则
    数组名a是整个二维数组的首地址,是常量,是行指针
    a+i是行指针,指向的是一整行,若对它加1则指向下一行
    (a+i)和a[i]一样,都是一个列指针即指向的是一个元素
    (a+j)+ j,a[i] + j,&a[i][j],都代表元素a[i] [j]的地址
    ((a+i)+j)、
    (a[i]+j)、(
    (a+j))[j]、a[i][j] 都代表元素a[i][j]的值

行 ————————>列(元素)——————>元素
二维——[]或*—— 一维——[]或*

  • 列指针变量画单行单列,出现行指针变量则画多行多列
  • 指向多维数组的指针变量
    常用于取二维数组a 元素地址的方式:&a[i] [j],a[i]+j,*(a+i)+ j

int a[3] [4];
int *p=&a[0] [3];
p+1指向元素a[1][0]
p+4指向元素a[1][3]
p-2指向元素a[0][1]

  • 指向由m个元素组成的一位数组的指针变量

对于普通的列指针变量定义就是int *p;(元素指针变量,普通指针变量)
定义行指针变量:类型 (*指针变量名) [m];
其中m是整数且为数组列数

int a[5][7];
int (*p)[7];//行指针变量
int *q=&a[2][2];//列指针变量
int *s=a;//不合法!因为a是行地址,而s 是普通的元素指针(列地址),不是行指针
  • 发现是二维数组,首先画二维表,然后等价代换(p就是a)

指向字符串的指针变量

  • 字符串在内存中的起始地址(第一个字符的地址)称为字符串的指针
  • 字符串的标志是双引号" ",结束标志是‘\0’,‘\0’等价于0,在循环中为假
  • 没有字符串变量来存字符串常量,一借字符数组,二借字符串指针
  • 字符指针变量存储的是字符串的首地址,字符数组中存储的是字符串本身
  • c语言中认为字符串就是连续占空间的首地址,就是第一个字符的地址且是地址常量

char str[]=“China”;合法,且数组长度为6(还有末尾的’\0’)
char *p=“China”;合法,字符串是地址,地址常量赋给地址变量,合法
str=“Chinese”;不合法!str是数组名,数组名是地址常量,不可以放在赋值号左边
p=“Chinese”;合法
char *p={“China”};不合法,p是字符型指针变量,指针变量应得地址,字符串的标志是一对双引号而不是一对花括号,所以加上花括号一定不正确,花括号是初始化的事
char str[]={“China”};合法,字符数组在赋值的同时初始化
char *p;*p=“China”;不合法,有*为内容值,不是读就是写,放在赋值号的左边是写,而右侧字符串是一个地址

  • %s要求后面必须是一个列地址
char s[20]="abcedefGHI";
char *p="1234567";
//将指针后移5个元素,然后从该元素开始输出,遇到'\0'结束输出,输出的是一串字符
printf("%s\n",s+2);//初始指向a,后移2个元素,输出cdefGHI
printf("%s\n",p+5);//初始指向a,后移5个元素,输出67
printf("%s\n",strcat(s+3,p+4));//字符串连接,输出edefGHI567
printf("%s\n",strlen(p+2));//计算有效字符的长度,不包括'\0',从所给地址指向的字符开始到\0结束
printf("%s\n",strcpy(p+3,s+9));//字符串拷贝,从p+3的位置开始输出
printf("%s\n",s);//从第一个字符开始将全部输出
scanf("%s",s+3);//scanf中的地址表列,内容变量加&,数组中的元素要加&,如果本身是一个地址就不用加&了,输入时遇到" "(空格)或回车都能停止接收,如果想要接收含有空格的字符串要用gets()
printf("%s\n",s);

指向函数的指针变量

  • 函数名是一个地址常量,是起始地址
  • 类型名 (*指针变量名)();//小括号是函数的标志,*是指针变量的标志
    因此函数指针在定义时应该有两个小括号,可以定义形参也可以不定义
  • 函数名是一个地址,不能传值。加上小括号,变成函数调用是接收值的
int max(int a,int b){ //省略 }
main()
{int x=1,y=2
int (*p)();//定义指向函数的指针变量
p=max;
max(x,y);//函数调用
p(x,y);//也可以写成(*p)(x,y);
}
  • 函数的指针变量只能指向函数的入口处(函数的首地址),不能指向函数中的某条指令,对指向函数的指针变量加1是没有意义的
  • 给指向函数的指针变量赋值时,只写函数名即可,不必写参数

小结

  • 一维数组名是1.所有元素连续占空间的首地址 2.是地址常量 3.就是第一个元素的地址,是一个列地址
  • 二维数组名是1.所有元素连续占空间的首地址 2.是地址常量 3.是一个行地址
  • 字符串是1.所有元素连续占空间的首地址 2.是地址常量 3.就是第一个字符的地址
  • 函数名是1.这段代码连续占用空间的首地址 2.是地址常量 3.是第一个程序单元的地址,是一个函数地址
  • int *p; p可以指向普通变量,一维数组元素,二维数组元素,字符串

返回指针的函数

  • 定义方式:
    类型名 *函数名(形参列表){ }
  • 除了void类型,函数都需要一个返回值,返回指针的函数一定要return 地址
  • 调用时:变量名=函数调用;(这个变量名一定是地址变量)
int *fun(int *x,int*y)//int * 一定要加return 地址
{
	if(*x<*y)
		return x;//有*为内容值,无*为地址
	else
		return y;
}
main()
{
	int a=7,b=8,*p,*q,*r;
	p=&a;//内容变量画房子,地址变量画指向,得谁地址指向谁
	q=&b;
	//函数调用一定要赋值给一个地址变量
	r=fun(p,q);//有*为内容值,无*为地址,
	printf("%d,%d,%d\n",*p,*q,*r);//有*为内容值,不是读就是写,放在赋值号前是写,其他是读
}

指针数组

  • 若一个数组中的所有元素均为指针类型(地址),则称为指针数组(地址数组)
  • 定义:类型名 *数组名[常量表达式]
    int *s[10];
    类比:行指针的定义:int (*p)[5];
    s是一个常量,p是行指针变量

指向指针的指针变量

  • 用来存放指针变量地址的指针变量称为指向指针变量的指针变量
  • 定义: 类型名 **指针变量

结构体与文件

  • 宏替换时,仅仅是将源程序中与宏名相同的标识符替换成宏的内容文本,并不对宏的内容文本做任何处理
  • 宏可以嵌套定义但是不可以递归定义
  • 字符串常量即双引号中的字符,不作为宏进行替换
#define XYZ "this is a test"
printf("XYZ")      //输出XYZ
  • 结构体类型也是类型,类型是用来定义变量的,最后在使用中也是使用变量
  • struct是声明结构体类型时所必须的关键字
  • struct Student 整体是一个结构体类型名
  • 结构体变量在内存中占用字节数为各成员占用字节数总和
  • 结构体变量不能整体引用,只能引用它的成员:结构体变量名.成员名
  • 在结构体中可以定义包含自身类型的指针成员,非指针成员的数据类型不能是自身结构体类型
  • 对于结构体中含有数组,数组名不能放在复制号的左边,所以不能使用 ‘.’ 进行赋值,而要用strcpy()
  • 结构体成员变量成员的访问:
    非指针类型:结构体变量名.成员名
    指针类型:结构体指针->成员名 或 (*结构体指针).成员名

  • 常用文件函数

定义文件指针FILE *指针名
打开文件fopen(文件名,打开方式)
关闭文件fclose(文件指针名)
将文件标志指向开头rewind(文件指针名)
移动文件指针位置fseek(文件指针,位移量,起始点)
起始点文件开始位置:SEEK-SET ,0;文件当前位置:SEEK-CUR,1;文件末尾位置:SEEK-END,2
测定文件位置标记的当前位置ftell(文件指针名)
判断是否到达文件尾部(为真则到达)feof(文件指针名)
从文件中读向文件中写
fgetc(文件指针名)fputc(字符,文件指针)
fgets(字符串,文件个数,文件指针)fputs(字符串,文件指针)
fscanf(文件指针,格式字符串,输入表列)fprintf(文件指针,格式字符串,输入表列)
fread(读取到的数据存放在程序中的变量的地址,读取的字节个数,读取的数据项个数,文件指针)fwrite(程序中存放数据的变量的地址,写入的字节个数,写入的数据项个数,文件指针)
  • 打开文件的方式:
    r:打开已经存在的文件,只读
    r+:打开已经存在的文件,可读可写
    w:创建新文件并写数据,如果文件存在则覆盖写
    w+:创建新文件并读写,文件存在则覆盖写
    a:打开已存在的数据并追加写,文件不存在则创建
    a+:同a,且可读
    t:打开文本文件
    b:打开二进制文件

  • 在对文件进行读取的时候,若先执行了一次读取但是并没有读取到文件末尾,则下次读取的时候就会从上次读取结束的地方继续向下读取,而不是从头开始

  • 文件指针在读取一次之后会自动后移
    比如文件中的内容是abcdefg,读取完a之后,文件指针会自动指向b,下次从b开始读,读完自动指向c

  • sizeof()是一个二级单目运算符,用于求出一个变量或者一个类型在内存中占的字节数(字符串要算’\0’)
    此处注意,指针变量永远占2字节

double a; sizeof(a);为8
double *b; sizeof(b);为2
double c[5]; sizeof( c );为40
double *pt[3]; sizeof(pt);为6

待更新...
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值