整理了一些C语言知识点👇
编程规范
关键字不允许作为标识符,标识符必须由字母或下划线开头,除开头外,其他位置都可以由字母下划线或数字组成。某些编译程序仅能识别前6个字符,故应该规范好标识符前6位。
数据类型——常量
整形常量:
ngNum=1000L; //加后缀L表示长整形
UnsignLongNum=500U; //加后缀U表示无符号整形
OctalNumber1=0123; //加前缀0表示八进制
AlgorismNumber1=123;//十进制不需要修饰
HexNumber1=0x3ba4; //加前缀0x表示十六进制(字母和后缀大小写通用)
整形数据都以二进制方式存放于内存,其数值是以补码形式表示的,正数的补码与其原码相同,负数的补码是将该数绝对值的二进制形式按位取反后再加1。
实型(浮点型)常量:
SciNum1=123.45; //科学计数方式:十进制的小数方法描述实型
SciNum2=5.458e-1; //指数方式:5.458*10^(-1)
FloatNum-1.2345e2F; //单精度类型
LongDoubleNum=5.348e-1L; //长双精度类型
DoubleNum=1.2345e2; //不加后缀,默认为double双精度类型
字符型常量
字符常量
#include<stdio.h>
int main()
{ //''表示定界符
putchar('H'); /*输出字符常量H*/
putchar('e'); /*输出字符常量e*/
putchar('l'); /*输出字符常量l*/
putchar('l'); /*输出字符常量l*/
putchar('o'); /*输出字符常量o*/
putchar('\n'); /*进行换行*/
return 0;
}
字符串常量
C语言中存储字符串常量时,系统会在字符串的末尾自动加一个“\0”,作为字符串的结束标志,此时字符串长度为字符数加1。一个或多个文字如也是一个字符串常量。
#include<stdio.h> /*包含头文件*/
int main()
{ //""表示定界符
printf("What a nice day!\n"); /*输出字符串*/
return 0; /*程序结束*/
}
转义字符
转义字符 | 意义 |
---|---|
\t | 横向跳到下一制表位置 |
\v | 竖向跳格 |
\b | 退格 |
\r | 回车 |
\f | 走纸换页 |
\\ | 反斜杠“ \ ” |
\’ | 单引号符 |
\" | 双引号符 |
\n | 换行符 |
\a | 鸣铃 |
\0 | 空字符 |
\ddd | 1~3位八进制所代表的字符 |
\xhh | 1~2位十六进制数所代表的字符 |
注意:在字符串中,一个转义字符(如:\ddd )占一个字符长度
符号常量
#define 标识符 常量 不带分号
数据类型——变量
32位平台下👇
类型 | 关键字 | 内存大小 | 从整数最高位开始输出,输出的数位上的数字正确的数位个数(即有效数位) | 范围 |
---|---|---|---|---|
有符号基本整型 | int | 4字节 | - | -2147483648~2147483647 |
无符号基本整型 | unsigned | 4字节 | - | 0~4294967295 |
有符号短整型 | short | 2字节 | - | -32768~32767 |
无符号短整型 | unsigned short | 2字节 | - | 0~65535 |
有符号长整型 | long | 4字节 | - | -2147483648~2147483647 |
无符号长整型 | unsigned long | 4字节 | - | 0~4294967295 |
单精度类型 | float | 4字节 | 四字节=8位(其中最多只有6位可以用作小数点后的数字部分,没用上的被用于整数部分) | -3.4E-38 ~3.4E+38 |
双精度类型 | double | 8字节 | 8字节=16位(其中最多只有6位可以用作小数点后的数字部分,没用上的被用于整数部分) | -1.7E-308~1.7E+308 |
长双精度类型 | long double | 8字节 | 不同编译器位数不同 | -1.7E-308~1.7E+308 |
字符型 | char | 1字节 | - | -128~127 |
无符号字符型 | unsigned char | 1字节 | - | 0~255 |
想输出超出位数的数字部分需要在占位符的%后加上 “.”+数字
但是,超过位数范围的部分输出的数字会出错
不同位数平台下同一数据类型占用内存大小不都相同,编程中可以利用sizeof()计算数据类型内存大小
变量的存储类别
数字后添加字母的作用
U表示该常数用无符号整型方式存储,相当于unsigned int;
L表示该常数用长整型方式存储,相当于long
F表示该常数用浮点方式存储,相当于float
数值前面加“0”的意义是该数值是八进制。
数值前面加“0x”的意义是该数值是十六进制。
补充:数值后面加“”H“、“h”的意义是该数值是用16进制表示的。数值后面加“”B“、“b”的意义是该数值是用2进制表示的。
auto 变量
用于定义一个局部变量为自动的,局部变量所在模块执行完后系统会自动释放auto变量的存储空间,故每次执行到定义该变量时,都会产生一个新的变量并对其重新初始化,事实上,关键字auto是可以省略的,如果无特别指定,局部变量的存储方式默认为自动的。
#include<stdio.h>
void AddOne()
{
auto int iInt=1; /*定义整型变量*/
iInt=iInt+1; /*变量加1*/
printf("%d\n",iInt); /*显示结果*/
}
int main()
{
printf("第一次调用:"); /*显示结果*/
AddOne(); /*调用Show函数*/
printf("第二次调用:"); /*显示结果*/
AddOne(); /*调用Show函数*/
return 0; /*程序结束*/
}
static 变量
static 变量为静态变量,可用于声明内部变量或外部变量,其初始化操作只在第一次执行时起作用,在随后的运行过程中其值为上一次该变量被操作后存储下来的值。由于定义在函数内部且其值可变,故相比全局变量更加“安全”
#include<stdio.h>
void AddOne()
{
static int iInt=1; /*定义整型变量*/
iInt=iInt+1; /*变量加1*/
printf("%d\n",iInt); /*显示结果*/
}
int main()
{
printf("第一次调用:"); /*显示结果*/
AddOne(); /*调用Show函数*/
printf("第二次调用:"); /*显示结果*/
AddOne(); /*调用Show函数*/
return 0; /*程序结束*/
}
register变量
寄存器存储类变量,存放在寄存器而非内存的局部变量,好处是可以提高程序的运行速度。实际上,只有早期的编译器会执行此修饰符,如今的编译器能比程序员做出更好的决定,所以很多C语言编译器会忽略register对变量的修饰。
如果想有效地利用寄存器register关键字,必须像汇编语言程序员那样了解处理器的内部结构,知道可用于存放变量的寄存器的数量,种类以及工作方式。但是,不同计算机对于这些细节可能是不同的,因此,对于一个具备可移植性的程序来说,registerd的作用并不大。
运算符与表达式
自动类型转换
自动类型转换原则
下面几种情况都发生自动类型转换:
1、算术运算式中,低类型能够转换为高类型。
2、赋值表达式中,右边表达式的值自动隐式转换为左边变量的类型,并赋值给他,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型比左边长时,将丢失一部分数据,丢失的部分按四舍五入向前舍入。
3、函数调用中参数传递时,系统隐式地将实参转换为形参的类型后,赋给形参
4、函数有返回值时,系统会隐式地将返回值的类型转换为返回值类型,赋值给调用函数。
5、在程序中将数据用printf函数以指定格式输出时,当要输出的盐据类型与输出格式不符时,便自动进行类型转换
注意:较长型数据转换成短型数据输出时,其值不能超出短型数据允许的值范围,否则转换时将出错。如:
long a=80000;
printf("%d",a);
运行结果为14464,因为int型允许的最大值为32767,80000超出此值,故结果取以32768为模的余数,即进行如下取余运算:
(80000-32768)-32768=14464;
6、所有浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也会先转换成double型,再作运算。
强制类型转换
float i=10.1f;
int j=(int)i;
在变量前使用包含要转换类型的括号,就对变量进行了强制类型转换
注意:高级别向低级别转换可能会出现数据的丢失,在使用强制类型转换时要注意
算术运算
笔记:两个int型相除的结果为int型,7/4的结果为1,舍去了小数部分,如果其中一个数为负数,会采取“向零取整”的方法,即向0靠拢取整,结果为-1。
自增/自减运算符:放在变量前面,那么变量在参加表达式运算之前完成自增或自减运算,放在变量后面,则自增或自减运算在变量参加了表达式运算之后完成。
算术运算符的结合性:当算术运算符的优先级相同时,结合方向为“自左向右”。
关系运算
关系运算符用于对两个表达式的值进行比较,返回一个真值 1 或假值 0
逻辑运算
C语言中,表达式的值非零,那么其值为真。非零的值用于逻辑运算,则等价于1;假值总是为0。
符 号 | 功 能 |
---|---|
&& | 逻辑与 |
丨丨 | 逻辑或 |
! | 单目逻辑非 |
位
位运算
符 号 | 名 称 | 功 能 |
---|---|---|
& | “与”运算符 | 使参与运算的两数对应的二进位相“与”,对应的两个二进位均为1则结果为1,否则为0(双目) |
丨 | “或”运算符 | 使参与运算的两数各对应的二进制位相“或”,只要对应的两个二进位有一个为1,结果位就为1(双目) |
^ | “异或”运算符 | 使参与运算的两数各对应的二进位相“异或”,当对应的两个二进位数相异时结果为1,相同时为0(双目) |
~ | “取反”运算符 | 对参与运算的各二进位按位求反,将0变成1,1变成0(单目,右结合性) |
&
清零操作:要将原数中为1的位置为0,只需使与其进行“与”操作的数所对应的位置为0便可实现清零操作。
取特定位:取后n位,则要与后n位均是1的数相“与”
|
归一操作:要将某几位为1,只需与几位是1的数执行“或”操作便可
^
用于简单的加密算法
要将后n位翻转,只需与后n位都是1的数进行“异或”操作即可
不使用临时变量的情况下实现两个变量值的互换。↓↓↓
x=x^y;
y=y^x;
x=x^y;
~
取反操作(单目)
移位运算
<<
“左移”运算符(双目),把“<<”左边的运算数的各二进位全部左移若干位,“<<”右边为移动的位数,高位丢弃,低位补0
如a<<2,假设 a=39,那么 a 在内存中的存储情况如下↓↓↓
|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0 |0|0|1|0|0|1|1|1| 移位前
|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0 |1|0|0|1|1|1|0|0| 移位后
>>
“右移”运算符(双目),把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边为移动的位数,在进行右移时,当为正数时,最高位补0;而为负数时,最高位补0还是补1取决于编译系统的规定。移入0的称为“逻辑右移”,移入1的称为“算术右移”。
注意:当移动的位数超过类型的长度时,会取余数,然后移动余数个位
对于10进制数字,左移一位在末尾加上一个0,数值变大10倍。同理,对于二进制数字,左移一位是在末尾加上一个0,数值变为原来的2倍,在左移运算中前提是移出位不含1,利用左移运算做数据*2的循环运算效率更高。
>>>
逻辑右移也叫无符号右移,运算规则:低位溢出,高位补0
循环移位
循环移位就是将移出的低位放到该数的高位,或者将移出的高位放到该数的低位
循环左移
z=x>>(32-n); //将x的左端n位先放到z中的低n位中
y=x<<n; //将x左移n位,其右边低n位补0
y=y|z; //将y与z进行按位“或”运算
循环右移
z=x<<(32-n); //将x的右端n位先放到z中的高n位中
y=x>>n; //将x右移n位,其左端高n位补0
y=y|z; //将y与z进行按位“或”运算
推荐热文:【技巧总结】位运算装逼指南
位段 略
逗号运算与逗号表达式
表达式1,表达式2,表达式3,...,表达式n;
逗号表达式的求解过程:先求解表达式1,再求解表达式2,一直求解到表达式n。整个逗号表达式的值是表达式n的值。
Value,1+2,5+7;
上面的语句中,Value所得到的值为7而非12,因为赋值运算符的优先级比逗号运算符的优先级高
Value=(2+5,1+2,5+7);
使用括号后,Value的值为12
复合赋值运算符
+= -= *= /=
a=a+1中,表达式a计算2次,对于a+=1,表达式仅计算1次,一般来说,这种区别对于程序的运行没有太大的影响,但是如果表达式中存在某个函数的返回值,那么函数将被调用两次。
运算符优先级和结合性表格
优 先 级 | 运 算 符 | 含 义 | 结 合 性 |
---|---|---|---|
1 | ( ) | 圆括号 | 自左向右 |
[ ] | 下标运算符 | ||
-> | 指向结构体成员运算符 | ||
. | 结构体成员运算符 | ||
2 | ! | 逻辑非运算符(单目) | 自右向左 |
~ | 按位取反运算符(单目) | ||
++、-- | 自增,自减运算符(单目) | ||
- | 负号运算符(单目) | ||
* | 指针运算符(单目) | ||
& | 地址与运算符(单目) | ||
sizeof | 长度运算符(单目) | ||
(类型) | 强制类型转换 | (数据类型)表达式 | |
3 | *、/、% | 乘法、除法、求余运算符 | 自左向右 |
4 | +、- | 加法、减法运算符 | |
5 | <<、>> | 左移、右移运算符 | |
6 | <、<=、>、>= | 小于、大于或等于、大于、大于或等于运算符 | |
7 | ==、!= | 等于、不等于运算符 | |
8 | & | 按位与运算符 | |
9 | ^ | 按位异或运算符 | |
10 | | | 按位或运算符 | |
11 | && | 逻辑与运算符 | |
12 | || | 逻辑或运算符 | |
13 | ?: | 条件运算符(三目) | 自左向右 |
14 | =、+=、-=、*=、/=、%=、>>=、<<=、&=、^=、|= | 赋值运算符 | |
15 | , | 逗号运算符(顺序求值运算符) | 自左向右 |
常用的数据输入/输出函数
字符
输出:putchar函数
putchar('A');
putchar('\101');//反斜杠和八进制数字构成八进制转义字符,'A'的ASCII码的八进制值为101
putchar(\n);
更多关于转义字符推荐文章:C语言中的转义字符
输入:getchar函数
int cChar=getchar();//字符可以赋給一个字符变量或整型变量
char cChar=getchar();
字符串
输出 语法格式:int puts(char *str);
puts("I LOVE CHINA!");
注意:puts函数会在字符串中判断“\0”结束符,遇到结束符时,后面的字符不再输出,并且自动换行。
输入 语法格式:char *gets(char *str);
作用是将读取的字符串保存在形式参数str变量中,读取过程直到出现新的一行为止,其中新一行的换行字符将会转换为空终止符“/0”。
注意:在输入字符或者字符串数据时以回车结束,当输入完数据后,要使用getchar函数接受回车符,否则下次使用getcharha函数时将得到回车符。
printf
printf(格式控制,输出列表)
格式字符 | 用于输出👇 |
---|---|
d,i | 带符号十进制整数 |
o | 无符号八进制整数 |
x,X | 无符号十六进制整数。x:a~f小写输出;X:大写输出 |
u | 无符号十进制整数 |
c | 字符,只输出一个字符 |
s | 字符串 |
f | 小数形式 |
e,E | 实数的指数形式。e:指数用“e”表示;E:指数用"E"表示 |
g,G | 判断并输出“%f” "%e"格式中宽度较短的一种形式,不输出无意义的0,若以指数形式输出,则指数以大写表示 |
附加格式说明字符
关于字符串:
“%10s”:“%ms”表示输出字符串占m列,如果字符串长度大于m,则突破限制将其全部输出,如果小于m,则用空格向左补齐;“%-ms”中负号表示右补空格
“%10.3s”: “%m.ns”表示输出占m列,但只取字符串左端n个字符且这n个字符出现在m列的右侧,左补空格;“%-m.s”中负号表示n个字符输出在m列内的左侧,右补空格,如果n大于m,则m自动取n值保证输出n个字符
在格式控制处使用“%%”可以输出“%”符号
控制小数位数推荐文章:
c语言控制输出格式-小数点位数
scanf
scanf(格式控制,地址列表)
格式字符 | 用于输入👇 |
---|---|
d,i | 有符号十进制整数 |
u | 无符号十进制整数 |
o | 无符号八进制整数 |
x,X | 无符号十六进制整数 |
c | 单个字符 |
s | 字符串 |
f | 实型,可以是小数形式也可以是指数形式 |
e,E,g,G | 与f作用相同(e,g大小写作用相同) |
附加格式 | 功能说明 |
---|---|
l | 用于输入长整型数据:%ld %lo %lx %lu 和double型的数据:%lf %le |
h | 用于输入短整型数据:%hd %ho %hx |
n(整数) | %nd 指定输入数据所占的宽度 |
* | *%d 表示指定的输入项在读入后不赋給相应的变量 |
选择结构
if
嵌套
在使用if语句嵌套时,应注意if与else的配对情况,else总是与其上面的最近的未配对的if进行配对
条件运算符
格式: 表达式1?表达式2:表达式3
检验表达式1的值,为真则返回表达式2的结果值,为假则返回表达式3的结果值
if(a>b)
{max=a;}
else
{max=b;}
改写👇
max=(a>b)?a:b
switch
switch(表达式)
{
case 情况1:
语句块1;
case 情况2:
语句块2;
...
case 情况n:
语句块n;
default:
默认情况语句块;
}
switch语句检验的表达式必须为整形,可以包含运算符和函数调用
case语句检验的值必须为整型常量,可以是常量表达式或常量运算
每个switch结构只能有一个default语句,而且default语句可以省略
在使用switch语句时,每一个case情况中都要使用break语句,break使得执行完case语句后跳出switch,如果case匹配成功了,但缺少break,程序还会继续向下执行 (default也会被执行),直到遇到break,return和swith结束为止
switch()
{
case 1:
表达式;
break;
/*多路开关模式*/
case 2:
case 3:
case 4:
表达式;
break;
case 5:
表达式;
break;
default:
表达式;
break;
}
循环控制
while 和 do…while
while(表达式)
{
语句1;
...
}
首先检验表达式,为假则退出while循环,为真则执行语句(块)后回到while处重新检验表达式,循环中有假才跳出循环
do
{
语句1;
...
}
while(表达式);
首先执行一次循环体语句,然后判断表达式,为真返回重新执行循环体语句,执行循环直到表达式的判断为假才结束循环
for
for(表达式1;表达式2;表达式3) 语句块
表达式1,2,3都可以省略,但是表达式之间的分号不能省略;表达式1和3可以使用逗号表达式
转移语句
goto
goto 标识符;
使程序立即跳转到函数内部的任意一条可执行语句,标识符是同一个函数内某条语句的标号,标号可以出现在任何可执行语句的前面,并且以一个“:”作为后缀
goto Show;
printf("the message before ShowMessage");
Show:
printf("ShowMessage");
//第一个printf不会被执行,第二个printf会执行
break
终止并跳出循环,继续执行后面的代码,只能用于循环语句或switch语句
continue
程序返回到循环头部继续执行,而不是跳出循环,continue语句只结束本次循环,而不是像break那样终止整个循环
for(1.初始化;2.条件判断;3.变量迭代)
{
if(4.某条件)
{
continue;
5.其他语句;
}
}
执行步骤为:
1 ----> 2 ----> 4条件满足 ----> ==5.后面的其他语句不执行,直接执行3迭代变量,然后进入下一轮循环条件判断2 ==----> 再判断4 ----> …
数组
常见数组
一维数组初始化:
#include<string.h>
memset(数组名,0,sizeof(数组名));
二维数组:
行下标可省,列下标不可省,系统可以根据后面的下标和元素个数确定第一个下标,正确写法👇
int iArray[][3]={1,2,3,4,5,6};
系统会根据数据的个数进行分配,共有6个数据,数组每行分为3列,当然可以确定数组为2行
也可以分行给数组元素赋值(不管是行下标还是列下标,其索引都是从0开始的)👇
int iArray[2][3]={{1,2,3},{4,5,6}};
多维数组的元素地址连续
字符数组:
char cArray[]={'H','e','l','l','o'};
char cArray[]={"Hello"};
char cArray[]="Hello";
输出字符数组:printf("%s",cArray);
当字符数组以单个字符进行赋值时,记得在数组的最后要加上‘\0’;
字符串处理函数
字符串复制
#include<string.h>
strcpy(目的字符数组名,源字符数组名);
把源字符数组中的字符串复制到目的字符数组中,字符串结束标志“\0”也一同复制;源字符数组名可以是一个字符串常量
字符串连接
#include<string.h>
strcat(目的字符数组名,源字符数组名);
把源字符数组中的字符串连接到目的字符数组中字符串的后面,并删去目的字符数组中原有的结束标志“\0”,目的字符数组应有足够长度用于连接字符串
字符串比较
#include<string.h>
strcmp(字符数组名1,字符数组名2);
将一个字符串与另一个字符串从首字母开始,按照ASCII码的顺序逐个进行比较:
字符串1=字符串2,返回值为0
字符串1>字符串2,返回值为正数
字符串1<字符串2,返回值为负数
字符串大小写转换
#include<string.h>
strupr(字符串);
将字符串中的小写字母转换成大写字母,其他字母不变
#include<string.h>
strlwr(字符串);
将字符串中的大写字母转换成小写字母,其他字母不变
获得字符串长度
#include<string.h>
strlen(字符数组名);
计算字符串的实际长度(不含字符串结束标志“\0”),函数返回值为字符串的实际长度
数组应用:反转输出字符串
#include <stdio.h>
int main()
{
int i;
char String[7] = {"mrsoft"};
char Reverse[7] = {0};
int size;
size = sizeof(String); /*计算源字符串长度*/
/*循环读取字符*/
for(i=0;i<6;i++)
{
Reverse[size-i-2] = String[i]; /*向目标字符串中插入字符*/
}
/*输出源字符串*/
printf("输出源字符串:%s\n",String);
/*输出目标字符串*/
printf("输出目标字符串:%s\n",Reverse);
return 0; /*程序结束*/
}
函数
参数
参数列表中存在多个同类型参数时,不可用类型定义+逗号,必须一一定义。
数组用作函数参数:用数组名作为实参,指向该数组的第一个元素的指针就被传递到函数中
被调用的函数的参数可以为可变长度数组或指针
void Function(iInt iArray[]);//参数为可变长度数组
int iArray[10];
Function(iArray);
void Function(int* pPoint);//参数为指针
int iArray[10];
Function(iArray);
函数调用
函数在表达式中被调用
iResult = iNum3 * AddTowNum(3,5); //在表达式用调用AddTwoNum函数
函数在参数中被调用
iResult = AddTwoNum(10,AddTwoNum(3,5)); //函数在参数中
函数嵌套调用:略
递归调用
#include<stdio.h>
void DisplayNames(char** cNameArray); /*声明函数*/
char* cNames[]= /*定义全局字符串数组*/
{
"Aaron", /*为字符串进行赋值*/
"Jim",
"Charles",
"Sam",
"Ken",
"end" /*设定结束标志*/
};
int main()
{
DisplayNames(cNames); /*调用递归函数*/
return 0;
}
void DisplayNames(char** cNameArray)
{
if(*cNameArray=="end") /*判断结束标志*/
{
return ; /*函数结束返回*/
}
else
{
DisplayNames(cNameArray+1); /*调用递归函数*/
printf("%s\n",*cNameArray); /*输出字符串*/
}
}
内部函数
static 返回值类型 函数名(参数列表);
内部函数只能在所在的源文件中使用,即使不同的源文件中有相同的函数名也没有关系
外部函数
extern 返回值类型 函数名(参数列表);
== C语言中定义函数时,如果不指明函数是内部还是外部,默认指定该函数为外部函数 ==
函数应用
#include<stdio.h>
#include<math.h> //包含头文件
int num1,num2;
long num3,num4;
float num5,num6;
double num7;num8;
num1=abs(num2); //求整数的绝对值
num3=labs(num4); //求长整形的绝对值
num5=fabs(num6); //求浮点数的绝对值
num7=sin(num8); //求解正弦
num7=cos(num8); //求解余弦
num7=tan(num8); //求解正切
#include<stdio.h>
#include<ctype.h> //包含头文件
char c;
isalpha(c); //检测字母(大写或小写),是则返回非零值,否则返回0
isdigit(c); //检测数字,是则返回非零值,否则返回0
isalnum(c); //检测字母或数字,是则返回非零值,否则返回0
指针
" & " 和 “ * ”的运算符优先级别相同,按自右向左的方向结合
类型 * 变量名; //类型说明表示本指针变量所指向的变量的数据类型
printf("%p",p); //输出指针地址
p++; //不是给地址加1,而是使指针指向下一个存放该类型变量的地址
int *p,a[10],i;
p=a; //数组的名称就是数组在内存中的首地址,可以赋值給指针变量,也可以用 p=&a[0];
printf("%d %d",*(p+i),*(a+i));//可用*(p+i),*(a+i)表示数组元素
int (*p)[5]; //定义指针p,指向一个整型的一维数组
返回指针值的函数
类型名 *函数名(参数列表);
通过指针引用二维数组:
*(a[0]+n) //表示第0行第n个元素
*(*(a+n)+m) //表示第n行第m列的元素
*(a[n]+m) //表示第n行第m列元素
一维指针数组
#include<stdio.h>
main()
{
int i;
char *month[]=
{
"January",
"February",
"March",
"......"
}; /*给指针数组中的元素赋初值*/
for(i=0;i<12;i++)
printf("%s\n",month[i]); /*输出指针数组中的各元素*/
}
指向指针的指针
类型 **指针变量名; //p指向另一个指针变量,该指针变量又指向一个变量,*运算符自右向左结合
指针数组作main函数的参数
main(int argc,char *argv[])
参数argc是命令行输入参数的个数,argv[]的大小参数决定,数组内存放了所有的命令行参数,利用该指针数组作main函数的形参,可以向程序传送命令行参数,C的源程序经过编译、链接后会生成扩展名为.mp4的可执行文件,该文件可以在操作系统下直接运行
输出main函数的参数内容:
#include<stdio.h>
main(int argc,char *argv[]) /*main函数为带参函数*/
{
printf("the list of parameter:\n");
printf("命令名:\n");
printf("%s\n",*argv);
printf("参数个数:\n");
printf("%d\n",argc);
}
指针变量加(类似数组的)下标
指针变量加上下标,代表的意义就是:指针中的值就是起始地址,下标则是从指针中存储的地址作为起始地址开始偏移的,实际就是一个数组元素
注意:对同一非空指针delete多次,只有第一次delete被正确执行,之后的delete全部发生异常。
结构体
结构体类型和结构体变量
struct 结构体名 //结构体类型的定义
{
成员列表
}; //不要忘记分号
struct 结构体类型名 结构体变量名;
以上可简写为👇
struct 结构体名 //结构体名可省略
{
成员列表;
}变量名列表; //可以定义多个变量
结构体数组
struct 结构体名
{
成员列表;
}数组名;
struct 结构体名
{
成员列表;
}
struct 结构体类型名 数组;
初始化操作
结构体变量
struct date
{
int year;
int month;
int day;
};
struct Student
{
char nName[20];
char cSex;
int iGrade;
struct date brithday;
}student1={"HanXue","W",3,{1986,12,6}}; //定义变量并初始化,数据顺序与结构体的成员列表顺序一致
//程序中在为包含结构体变量的结构体变量初始化时要注意,使用大括号将赋值的数据包含在内
结构体数组
struct 结构体名
{
成员列表;
}数组名={初始值列表}; //每个数组元素内的数据要与结构体成员列表的顺序一致
//定义结构体数组时,可以不指定数组元素个数
//编译器会根据后面的初始值列表判断数组的元素个数
结构体指针
结构体类型 *指针名;
引用结构成员时,*pStruct一定要使用括号,因为点运算符的优先级是最高的 → (*pStruct).变量名
指向结构体数组的结构体指针
struct Student* pStruct;
pStruct=student; //student表示数组的第一个元素的地址,所以指针指向数组的首地址
一般用指向结构体变量的指针作为函数参数,如果直接传递结构体变量或结构体变量的成员,形参也要占用内存,增加了时间和空间开销,且实参不会因形参改变而改变
浅谈堆与栈
堆用来存放动态分配内存空间,而栈用来存放局部数据对象、函数的参数
#ifdef及#ifndef命令
#ifdef 宏替换名
语句段
#endif //endif命令用来表示#if段的结束
含义:如果宏替换名已被定义过,则对“语句段”进行编译,否则不编译
#ifdef 可与#else 连用👇
#ifdef 宏替换名
语句段1
#else
语句段2
#endif
#ifndef 与#ifdef 相反👇
#ifdef 宏替换名
语句段
#endif
含义:如果宏替换名没有被定义过,则对“语句段”进行编译,否则编译
#undef 宏替换名
含义:#undef命令可以删除实现定义好的宏定义
#define MAX_SIZE 100
char array[MAX_SIZE];
#undef MAX_SIZE //直到遇到#undef语句之前的语句都编译