(用于嵌入式基础巩固,是本人通过学习进行笔记记载,文章有不足指出,望各位博友指点一二,希望大家与我共进步)
VMware:虚拟机
Ubuntu:Linux操作系统中的一种
windows: GUI设计非常完善 用户多 系统不够稳定 不够安全 收费 不开源
Linux: GUI设计完善 免费 开源 需要一定的学习 一切皆文件
①pwd :获取当前目录的绝对路径(从根目录/开始)
②ls : 查看文件
ls 默认当前目录
ls <指定路径> 查看指定路径下的文件
ls -l 查看文件的详细信息
ls -a 查看所有文件
③cd进入某个目录
cd默认进入当前用户的工作目录(~)
cd-回到上一次所在的目录
cd ..回到上一级所在目录
cd<指定路径>进入到指定目录
④mkdir<文件名>创建一个文件夹
⑤rmdir<文件名> 删除一个空文件夹
⑥rm删除文件/unlink
unlink命令用于删除文件和链接,其中链接包括软链接和硬链接。它是最简单的命令之一,除了--help以及--version以外,没有任何其他选项
使用unlink删除文件,可以这样:
unlinkfilename
删除链接,可以这样使用:
unlinklink_name
删除成功后,不会有任何输出
unlink不能同时删除多个文件和链接
rm命令可以一次删除多个文件。但是unlink却不能,它一次只能删除一个文件或者链接,而且,也不能使用全局模式(globbingpatterns)。
使用unlink不能删除目录
GNU实现下的unlink命令是无法删除目录的,如果给定的文件名称是一个目录,就会报错
⑦farsight@ubuntu:~$
用户名 @主机名:路径(~) 命令提示符($)
⑧vi/vim/touch/gedit 文件名
vi/vim文件名: 创建文件/打开文件
命令行模式:
复制(nyy,从光标所在行开始复制n行)、粘贴(p,在光标下一行开始粘贴)、剪切(ndd)、撤销(u)...
按i:在光标所在位置插入、a:在光标之后插入、o:在下一行插入 ,进入插入模式
shift+: 进入底行模式
插入模式:
编辑文本内容
按Esc进入命令行模式
底行模式:
操作文件保存(w)、退出(q) x(保存并退出)
按Esc进入命令行模式
ctrl+d:表示结束输入
#include
gcc xxx.c -o xxx -lm -l:表示链接某个库 m:math库的名称
进制转换: 2^0=1 2^1=2 2^2=4 2^3=8 2^4=16 2^5=32 2^6=64 2^7=128 2^8=256
二进制转10进制: 10101 = 12^4+12^2+1*2^0
二进制转8进制: 100010010 = 0422 (前面0表示8进制)
二进制转16进制: 11010100101b = 0x6a5 (前面0x表示16进制)
十进制转二进制: 凑数法: 将十进制数凑成不同2的整数次幂相加
550 = 512 + 32 + 4 + 2 = 2^9 + 2^5 + 2^2 + 2^1 = 1000100110
表示字符时:' ' 、字符串:" "
'0' 0 "0"
常量: 在程序执行的整个过程中不允许(不能)改变的量
变量: 在程序执行的整个过程中允许(能)改变的量
C语言大小写敏感(区分大小写),关键字必须为小写
#include
intmain()
{
intsum=1+2+3+4+5+6+7+8+9+10;
printf("%d\n", sum);
}
非0为真
整型数据在内存中都是按照补码存储
符号位: 数据的最高位为符号位,0为正1为负
原码: 数据本身的二进制
反码: 符号位不变,其它位按位取反
补码: 正数的补码就是其原码,负数的补码是它的反码加1
0没有正负之分
00000000 +0
128:10000000 -0 //-0表示最小(它的值域范围内)的负数-1
1000000000000000-(2^15-1)-1->-2^15
-2^31
255: 补码: 11111111
反码: 11111110
原码: 10000001
151:128+16+4+2+1
补码:10010111
反码:10010110
原码:11101001
-105
char a=127+1
char a=1;
00000001-->11111111-->100000000-->00000000
标识常量主要注意它是原样替换不会改变运算优先级
数据类型 |
字节数 |
取值范围 |
char |
1 |
-128~127 |
unsigned char2 |
1 |
0~255 |
short |
2 |
-32768~32767 |
unsigned short |
2 |
0~65535 |
int |
4 |
-2147483648~2147483647 |
unsigned int |
4 |
0~4294967295 |
long |
4 |
-2147483648~2147483647 |
unsigned long |
4 |
0~4294967295 |
long long |
8 |
-9223372036854775808~9223372036854775807 |
unsigned long long |
8 |
0~18446744073709551615 |
float |
4 |
1.1754910^-38~3.4028210^38 |
double |
8 |
2.2250710^-308~1.7976910^308 |
long double |
12 |
2.2250710^-308~1.7976910^308 |
变量的一般形式: <存储类型> <数据类型> <变量名>;
初始化: 在变量定义时就赋初始值
赋值: 在定义之后再去给变量一个值
int a;
a=10; //赋值
int b=20; //初始化
int a=10, b=20; //初始化
a=b; //赋值
int a=10, b=a; //初始化
int b=a, a=10; //错误, a未定义
强制数据类型的转换是指采用某种方式将某种数据类型强制转换成指定的数据类型。这种转换存在两种方式:一种为显式的数据类型转换,另一种为隐式的数据类型转换。
显式的数据类型转换实现的一般形式为: (数据类型名称)< 表达式 >
在C语言中char和short在参与算术运算时都会自动变为int
char a='A'+2
short b=a+2;
int a= (int)(4.9+1.1);
float b=3.14;
int a= (int)b+3;
b=3.14
/ % ++ -- + - *
/: 如果左右两边都是整数,它代表的是整除,如果左右两边只要有一个是浮点数, 则结果一定是浮点数
%:它只能用于整数运算,求的是余数
++:前++表示先自己加1,再参与运算 后++表示先参与运算,再加1
int a=10, b;
b=++a;
printf("%d %d\n", a, b); // 11 11
int a=10, b;
b=a++;
printf("%d %d\n", a, b); // 11 10
--:同上,只不过加1变成减1
&& || !
&&:左右两个操作数都为真,结果为真,如果左右操作数有一个或都为假,结果一定为假短路法则:
int a=0, b=1, c;
c=a && ++b;
printf("%d %d %d\n", a, b, c); //0 1 0
test:
int a=0, b=1, c;
c=a++&&++b;
printf("%d %d %d\n", a, b, c);
int a=0, b=1, c;
c=++a&&++b;
printf("%d %d %d\n", a, b, c);
||:左右两个操作数都为假,结果为假,如果左右操作数有一个或都为真,结果一定为真
int a=1, b=1, c;
c=a||++b;
printf("%d %d %d\n", a, b, c); //1 1 1
test:
int a=1, b=1, c;
c=a--||++b;
printf("%d %d %d\n", a, b, c); //0 1 1
int a=1, b=1, c;
c=--a||++b;
printf("%d %d %d\n", a, b, c); //0 2 1
!:操作数为真结果为假,操作数为假结果为真
int a=0, c=1;
c=!a; //假
& | ~ ^ << >>
&:位与,双目运算符,左右操作数按位进行与操作
char a=0x10, b=071, c;
c=a&b;
=10000&111001
=010000&111001
=010000
=0x10
|:位或,双目运算符,左右操作数按位进行或操作
char a=20, b=66, c;
c=a|b;
=10100|1000010
=0010100|1000010
=1010110
=64+16+4+2
=86
~:位反,单目运算符,把操作数按位取反
char a=77, c;
c=~a;
=~(64+8+4+1)
=~1001101
=0110010
=32+16+2
=50
^:异或,双目运算符,左右操作数按位进行异或(相同为0不同为1)操作
char a=14, b=51, c;
c=a^b;
=1110^110011
=001110^110011
=111101
=32+16+8+4+1
=61
test:
//只能针对整数
int a=3, b=4;
a^=b->a=a^b->a=011^100->a=111
b^=a->b=b^a->b=111^100->b=011->b=3
a^=b->a=a^b->a=111^011->a=100->a=4
a=a+b;
b=a-b;
a=a-b;
<<:左移,双目运算符,把左操作数向左移动右操作数这么多位
unsignedchara=151, c;
c=a<<3;
= (128+16+4+2+1) <<3
=10010111<<3
=10111000
>>:右移,双目运算符,把左操作数向右移动右操作数这么多位
unsigned char a=151, c;
c=a>>3;
= (128+16+4+2+1) >>3
=10010111>>3
=00010010
注意:
移位操作时,目标数据本身不会发生改变
有符号移位: 符号位不变,看目标数据的正负
正数:
01111111<<1->01111110
01111111>>1->00111111
负数:
10001111<<1->10011110
10001111>>1->11000111
思考:
1.如何把某个变量低4位快速清0?
char a;
a&0xf0; //a & (~(0xf))
[20:23]
b& (~(0xf<<20))
2.如何把某个变量高4位快速置1?
char a;
a|0xf0;
[20:23]
b| (0xf<<20)
int a=3;
a*=3+2;
a=a* (3+2)
sizeof int
putchar('A');
putchar(65);
成功返回打印字符的ASCII失败返回-1
puts();
参数要求是字符串的起始地址;
输出参数代表的字符串遇到'\0'停止,会在输出结束后自动添加'\n'
返回值代表实际输出的元素个数(包含'\0')
printf();
不定参数的函数
格式控制符和修饰符需要大家记忆。
转换说明 |
输出 |
%a,%A |
浮点数、十六进制数和p-计数法(C99) |
%c |
一个字符 |
%d |
有符号十进制数 |
%e,%E |
浮点数,e计数法 |
%f |
浮点数,十进制计数法 |
%g,%G |
根据数值不同自动选择%f或%e,%e格式在指数小于-4或者大于等于精度时使用 |
%i |
有符号十进制整数(与%d相同) |
%o |
无符号八进制整数 |
%p |
指针 |
%s |
字符串 |
%u |
无符号十进制数 |
%x,%X |
使用十六进制数0f的无符号十六进制整数 |
%% |
打印一个百分号 |
prinf()修饰符
修饰符 |
意义 |
标志 |
五种标志将在后面的表中说明,可以使用零个或者多个标志示例: "%-10d" |
digit(s) |
字段宽度的最小值。如果字段不能容纳要打印的数或者字符串,系统会使用更宽的字段示例: "%4d",“%10s” |
.digit(s) |
精度.对于%e,%E和%f转换,是将要在小数点的右边打印的数字的位数。对于%g和%G转换,是有效数字的最大位数。对于%s转换,是将要打印的字符的最大数目。对于整数转换,是将要打印的数字的最小位数。如果必要,要使用前导0来达到位数。只使用"."表示其后跟随一个0,所以%.f和%.0f相同示例: “%5.2f”表示打印一个浮点数,它的字段宽度为5个字符,小数点后有两个数字 |
h |
和整数转换说明符一起使用,表示一个short int或unsigned short int类型数值示例: “%hu”, "%hx", "%6.4hd" |
hh |
和证书转换说明符一起使用,表示一个signed char或unsigned char类型数值 |
j |
和整数转换说明符一起使用,表示一个intmax_t或uintmax_t值示例: "%jd","%8jx" |
l |
和整数转换说明符一起使用,表示一个long int或unsigned long int类型值 |
ll |
和整数转换说明符一起使用,表示一个long long int或unsigned long long int类型值(C99)示例: "%lld","%8llu" |
L |
和浮点数转换说明符一起使用,表示一个long double值示例: "%Lf", "%10.4Le" |
t |
和整数转换说明符一起使用,表示一个ptrdiff_t值(与两个指针之间的差相对应的类型)(C99)示例: "%td", "%1ti" |
z |
和整数转换说明符一起使用,表示一个size_t值(sizeof返回的类型)(C99)示例: "%zd","%12zx" |
printf()的标志
标 志 |
意义 |
- |
项目左对齐,即,会把项目打印在字段的左侧开始处示例: "%-20s" |
+ |
有符号的值若为正,则显示带加号的符号;若为负,则显示带减号的符号示例: "%+6.2f" |
(空格) |
有符号的值若为正,则显示时带前导空格(但是不显示符号);若为负,则带减号符号。+标志会覆盖空格标志示例: "% 6.2f" |
# |
使用转换说明的可选形式。若为%o格式,则以0开始;若为%x和%Xgeshi ,则以0x或0X开始。对于所有的浮点形式,#保证了即使不跟任何数字,也打印一个小数点字符。对于%g和%G格式,它防止尾随0被删除示例: "%#o", "%#8.0f", "%+#10.3E" |
0 |
对于所有的数字格式,用前导零而不是空格填充字段宽度。如果出现-标志或者指定了精度(对于整数)则忽略该标志示例: "%010d", "%08.3f","%02X" |
getchar();
无参数
从标准输入中获取一个字符,并通过返回值返回其ASCII
阻塞的函数
gets();
chara[100];
gets(a);
scanf();
结束标志: 空格 回车 tab
解决脏字符方法: ①getchar() ②%*c
①简化的形式(如果不加{},则条件只包含一句)
if(条件表达式)
{
语句1;
...
语句1;
}
②阶梯形式:
if(){
}elseif(){
}elseif(){
}else{
}
③ 嵌套式:
if(){
if(){
}else{
}
}else{
if(){
}else{
}
}
switch(整数或整数表达式){
case常量或常量表达式1:
语句;
break;
case常量或常量表达式2:
语句;
break;
case常量或常量表达式3:
语句;
break;
...
case常量或常量表达式n:
语句;
break;
default:
语句;
break;
}
for(表达式1;表达式2;表达式3){
循环体;
}
执行此次循环时,表达式1只会执行1次;
先执行表达式2,为真则执行循环体,为假退出循环;
循环体执行完成,执行表达式3 表达式3执行完成,再执行表达式2。
for(int i=0; i<10; i++);
注:
表达式都可以省略for(;;); //代表死循环
嵌套for循环:
for(表达式1;表达式2;表达式3){
for(表达式4;表达式5;表达式6){
循环体;
}
}
1、执行表达式1
2、执行表达式2,表达式2为假就退出循环,为真继续执行
3、表达式4
4、执行表达式5,表达式5为假,执行表达式3
再执行表达式2,表达式2为假退出循环
为真继续执行第3步
5、执行循环体
6、执行表达式6
7、跳到第4步执行
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
printf("i:%d j:%d\t", i, j);
}
puts("");
}
i:0j:0 i:0j:1 i:0j:2
i:1j:0 i:1j:1 i:1j:2
i:2j:0 i:2j:1 i:2j:2
while(表达式1){
循环体;
}
表达式1为真则执行循环体,然后继续执行表达式1...为假退出while循环
while(1);//死循环
do{
循环体;
}while(条件表达式)
先执行一次循环体,再执行条件表达式,为真继续执行循环体...为假退出循环
它只能在同一个函数内部跳转
int main()
{
puts("111111111");
puts("222222222");
gotoxxx;
puts("333333333");
xxx:
puts("444444444");
}
break: 跳出本层循环或者switch语句
continue:结束本次循环直接进入下一次循环
数组:同种数据类型的有序集合
<存储类型> <数据类型> <数组名>[常量或常量表达式]; //[]变址运算符
int arr[10]; //在内存中开辟10个元素空间,每个空间都是一个int变量
在数组定义时,将每个元素都赋值
int arr[5] = {1,2,3,4,5};
在数组定义时,将部分数据初始化,其它的数据会被系统自动初始化为0
char arr[5] = {'a','b'};
int arr[10] = {0}; //定义时清空数组
在数组定义时,指定下标赋值,其它的数据会被系统自动初始化为0
short arr[10] = {[1] =10, [7] =20};
数组具备自动计数的功能: (数组必须要初始化)
int arr[] = {1,2,3,4}; //系统会自动根据初始化元素个数决定给该数组分配多大空间
如上所示:系统会给该数组分配4*sizeof(int)字节
C语言对数组不做越界检查:
C语言编译器在编译程序不对数组做越界检查,所以我们在使用数组时需要注意不要越界
int arr[5] = {1,2,3,4,5};
arr[10] =10; //语法不会报错,但是这个数据越界了
数组下标是一个变量,且不允许初始化
int n;
int arr[n]; //int arr[n] = {1,2,3,4,5}
只能对数组元素一个一个的赋值;
int arr[5];
arr[0] =0;
arr[1] =1;
...
arr[4] =4;
//arr[5] = {0,1,2,3,4}; //err
通过循环实现数组的遍历
int arr[5] = {1,2,3,4,5};
for(int i=0; i<5; i++){
printf("%d ", arr[i]);
}
puts("");
数组元素从键盘录入:
int arr[5] = {1,2,3,4,5};
for(int i=0; i<5; i++){
scanf("%d", &arr[i]);
}
#include
intmain(int argc, char* argv[])
{
int n;
printf("input num: ");
scanf("%d", &n); //通过键盘输入
int a[n];
for(int i=0; i
#include
intmain(int argc, char*argv[])
{
int a[5] = {1,2,3,4,5}, tmp;
for(int i=0; i<5; i++)
printf("%d ", a[i]);
puts("");
//1、用tmp保存最后一个元素的值
tmp=a[4];
//2、将数组元素依次向右移动1位
for(int i=4; i>0; i--){
a[i] =a[i-1];
}
//3、将tmp保存的值赋值给a[0]
a[0] =tmp;
for(int i=0; i<5; i++)
printf("%d ", a[i]);
puts("");
return 0;
}
charstr[20] ="hello world";
charstr[20] = {'h','e','l'};
gets(str); //scanf("%s", str);
一串单个的字符的集合 "abcd" 字符串末尾一定有'\0' 用字符数组存储字符串
char a[] ="abc"; sizeof a==4;
//char a[4] = "abcd"; //err
char a[20];
//a[0] = "你"; //err
char b[] ="你";
for(int i=0; b[i]; i++)
a[i] =b[i];
字符串中第一个'\0'之前的字符叫做有效字符
chara[] = {'a','b','c','\0','d','c','\0'};
sizeof a==7;
puts(a); //abc就是有效字符
#include
size_t strlen(const char*s);
const char*s: 要求字符串长度的目标字符串(传递一个字符串的首地址)
size_t: 目标字符串的有效长度
(在32位系统中size_t代表unsigned int类型; 在64位系统中size_t代表long unsigned int类型)
有a,b两个字符串,把b字符串的内容拷贝a
#include
实现字符串的完全拷贝
char* strcpy(char*dest, const char*src);
char*dest: 要拷贝的字符串的首地址
const char*src: 要拷贝的目标字符串(被拷贝的字符串)
char*: 返回要拷贝的字符串的首地址
注意:
第一个参数不能是字符串常量,只能是一个字符数组或malloc开辟堆空间的首地址
第一个参数所在空间的大小应该大于第二个参数代表字符串的有效长度,否则会访问越界
拷贝第二个参数代表字符串的前n个字符到第一个参数所带表的空间
char*strncpy(char*dest, const char*src, size_t n);
size_t n: 要拷贝目标字符串的前n位
char*strcat(char*dest, const char*src);
char*dest:要拼接的第一个字符串首地址
const char*src:要拼接的第二个字符串首地址
char*:返回拼接后字符串的首地址
注意: 第一个参数不能是字符串常量,只能是一个字符数组或malloc开辟堆空间的首地址
第一个参数所在空间的大小应该大于或等于两个字符串有效字符之和
把第二个字符串前n个字符拼接在第一个字符串之后
char*strncat(char*dest, const char*src, size_t n);
比较两个字符串是否完全一致(比较两个字符串的有效字符,不会改变两个字符串)
int strcmp(const char*s1, const char*s2);
返回值:
0表示两个字符串完全一致
非0: 两个字符串不同
比较两个字符串的前n个字符
int strncmp(const char*s1, const char*s2, size_t n);
int a[5] = {3,1,2,7,6};
第一步:
找出最大的元素并且把它放到数组最后(从小到大):
a[0] >a[1] 交换a[0] 和a[1]的值
for(int j=0; j<5-1; j++)
{
if(a[j] >a[j+1])
{
a[j] ^=a[j+1];
a[j+1] ^=a[j];
a[j] ^=a[j+1];
}
}
第二步:
继续找出第二大的数:
重复第一步的过程:
for(int i=0; ia[j+1])
{
a[j] ^=a[j+1];
a[j+1] ^=a[j];
a[j] ^=a[j+1];
}
}
}
#include
#include
int main()
{
char str[100];
printf("please input string: ");
gets(str);
int n=strlen(str);
for(int i=0; istr[j+1])
{
str[j] ^=str[j+1];
str[j+1] ^=str[j];
str[j] ^=str[j+1];
}
}
}
puts(str);
}
二维数组:有两个下标的数组
<存储类型> <数据类型> <数组名>[行标]*[列标];
int a[2][3] = {1,2,3,4,5,6};
int b[2][3] = {{1,2,3},{4,5,6}};
int a[2][3] = {1,2,3}; //第二行补0
int b[2][3] = {0}; //均为0
int a[][3] = {1,2,3,4,5,6,7,8}; //int a[3][3],只能省略行标不能省略列标
int b[][3] = {1,2,3,4};
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int i=0; i<3; i++) //外层循环表示行,内层循环表示列
{
for(int j=0; j<4; j++)
{
printf("%d ", a[i][j]);
}
puts("");
}
int main(int argc, char*argv[])
{
int a[3][4], max, x=0, y=0;
for(int i=0; i<3; i++)
{
for(int j=0; j<4; j++)
{
printf("input a[%d][%d]: ", i, j);
scanf("%d", &a[i][j]);
}
}
max=a[0][0];
for(int i=0; i<3; i++)
{
for(int j=0; j<4; j++)
{
if(max
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
....
a[i][0] ==1 a[i][i] ==1
i>1, j>0&&j
#include
int main(int argc, char*argv[])
{
int n;
puts("input num: ");
scanf("%d", &n);
int a[n][n];
for(int i=0; i
char str[3][10] = {"abc", "hello", "welcome"}; //字符串长度不能超过列数
char dst[3][10];
键盘录入:
gets(a[i]);
在计算机内部存储器(内存)中,以字节为单位,每个字节都有一个编号,这个编号叫做地址。指针就是地址。
指针变量:存储地址的变量
普通变量:存储的是数据
第一类:创建指针变量,“ 基本类型+ * ”。
int* iptr;
char* cptr;
double* dptr;
第二类:给指针变量赋值,“ &+变量 ”。
int a=100;
char b='o';
double c=100.5;
int* iptr;
char* cptr;
double* dptr;
iptr=&a;
cptr=&b;
dptr=&c;
第三类:取指针变量保存的地址中的值,“ * + 指针变量”。
int a=100;
int*iptr=&a;
int b=*iptr;
于是,上面的代码改写成:
void swap(int*x, int*y)
{
int tmp=*x;
*x=*y;
*y=tmp;
}
void change()
{
int a=100;
int b=200;
swap(&a, &b);
}
地址、指针变量都叫指针
<存储类型><数据类型>*<指针变量名>
int*pa(野指针:不知内容是什么)
int a=10;
int*pa=&a;
char*ps=NULL;//NULL空指针 NULL是0的宏 //char是字符变量或者字符串
double a=3.14;
double*pa;
pa=&a;
int a=10, b=20;
int*p=&a;
p=&b; //更改指针变量P的指向 指针变量是可改变的
int a=10 , b=20;
int*p;
p=&a; //a的地址给了p
*p==a==10
a=15;
printf("%d %d\n",a,*p); //a = 15 *p = 15
*p=30;
printf("%d %d\n",a,*p); //a = 30 *p = 30
注意:在使用指针时,当脱离定义后,指针变量无表示的是地址,有表示具体的数据
px+n :结果表示从px存储的地址开始向地址大的方向偏移n个元素
偏移n*sizeof(指针指向的数据类型)字节
px-n :结果表示从px存储的地址开始向地址小的方向偏移n个元素
偏移n*sizeof(指针指向的数据类型)字节
px++ :px向地址大的方向偏移一个元素,且px指向这个新(偏移后)的地址
px-- :px向地址小的方向偏移一个元素,且px指向这个新(偏移后)的地址
Px-py: px和py指向变量的类型必须一致,结果表示两个指针之间间隔元素的个数(间隔字节/sizeof(元素的类型))
表达式 |
含义 |
p++或(p++) |
首先计算表达式的值为*p,然后p自增1 |
(*p)++ |
首先计算表达式的值为p,然后p自增1 |
++p或(++p) |
首先将p自增1,然后计算表达式的值为*p |
++p或++(p) |
首先将(p)自增1,然后计算表达式的值为(p) |
关系运算比较的实质是地址编号的大小
非NULL为真
char a[10] ="abcdef";//位数是从0开始,加1即为'b'
char*p=&a[0];
*p=='a';
*(p+1) =='b';
*(p+3) =='d';
int a[5] = {1,2,3,4,5}, *p=&a[0];
for(int i=0;i<5;i++);
printf("%d", *(p+i));
int a[4] = {1,2,3,4}, *p ,*q;
p=q=a;
q++;
printf("%d\n", q-p); //=1
printf("%d\n",(char*)q-(char*)p);//4
*和[]等价
*和&互为逆运算
[]和&互为逆运算
int a[10], *p=a;
0=
int (*a)[5];
//指向数组(这里每个一维数组含5个元素)的指针,a是第一个一维数组的首元素地址,a+1指向第二个一维数组的首元素地址......(a是数组指针);
本质上是一个指针,只不过这个指针指向的是整个数组
<存储类型> <数据类型> (*指针名)[下标];
char (*p)[3]; //下标代表该指针可以指向具备该下标这么多个元素的数组
int a[4] = {1,2,3,4},b[4] = {4,3,2,1};
int (*p)[4] =&a;
p=&b;
for(int i=0; i<4; i++)
printf("%d ", (*p)[i]); // *p == *&b --> *p == b --> (*p)[i] == b[i]
注意: 对一维数组的数组名取地址表示整个一维数组的地址
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4];
p=a;
for(inti=0; i<3; i++){
for(intj=0; j<4; j++)
printf("%d ", (*(p+i))[j]); //p[i][j] *(*(p+i)+j)
puts("");
}
本质是一个数组,只不过这个数组里面每一个元素都是指针
<存储类型> <数据类型> *数组名[下标];
int*a[3]; //代表数组a中有3个int *的指针
int a=10, b=20, c=30;
int* arr[3] = {&a, &b, &c};
*arr[0] ==a ==10;
char a[100] ="one", b[100] ="world!", c[100] ="welcome";
char*arr[3] = {a, b, c};
*(arr[0]+1) ==a[1] =='n';
*arr[0] ='N'; //OK a[0] = 'N';
char*p="hello";
char*arry[4] = {"abc", "defg", "bbbbb", "cccccc"};
puts(arry[0]);
*(arry[0]+1) =='b';
//*arry[0] = 'A'; //err
int a[2][3] = {1,2,3,4,5,6};
int*arr[2] = {a[0], a[1]};
for(inti=0; i<2; i++){
for(intj=0; j<3; j++)
printf("%d ", *(arr[i]+j)); //arr[i][j] *(*(arr+i)+j) (*(arr+i))[j]
puts("");
}
#include
//声明函数
//a, b是形参
int add(inta, intb);
int main(int argc, char*argv[])
{
//int a = 1, int b = 2;
//1,2是实参
int sum=add(1,2);
return 0;
}
//函数的实现
int add(int a, int b)
{
//int sum = a+b;
//return sum;
return a+b;
}
指向指针的指针,一个二级指针变量可以存放一级指针的地址
<数据类型> **二级指针名;
int**p;
int a=10;
int*pa=&a;
int**pp;
pp=&pa;
pp==&a -->*pp==*&pa==pa==&a-->**pp==**&pa==*pa==*&a==a
二级指针和二维数组的关系?
没有关系!!!
具有特定功能的代码模块
<存储类型> <数据类型> 函数名(形式参数列表)
{
函数体;
return;
}
void ptr(void); //一般放在文件的开头(main函数之前)
ptr(); //在需要实现对应功能的地方调用函数
void ptr(void) //一般在main函数之后
{
puts("你睡醒了吗?");
}
总结:当实参不希望被被调函数修改的时候,可以使用函数的参数传递
当实参希望被被调函数修改的时候,要使用函数参数的地址传递
也分为值传递和地址传递(本质上都是地址的传递,所以不能通过sizeof(数组名)/sizeof(数组某个元素)来计算数组元素的个数)
void fun(inta[], intn);
void fun(int*a, intn);
在给函数传递字符串时,只需要传递首地址(字符串末尾有'\0')函数中定义的变量它的作用域在函数内部,生命周期开始于函数调用,结束于函数调用结束
void fun(int n, int m, int a[][n]);
void fun(int n, int m, int (*a)[n]);
//void fun(int n, int m, int **a); //**a是二级指针 传值时不能传递二维数组
本质上是一个函数,只不过返回值是一个指针
<数据类型> *<函数名>(形参列表) {
函数体;
return 地址;
}不能返回局部变量的地址
注意:函数的地址就是函数名
本质上是一个指针,只不过这个指针指向的是一个函数
<数据类型> (*<函数指针名>)(形参列表);
int add(int a, int b);
int (*p)(int, int); //p就是一个指向返回值为 int且参数有两个int类型的函数
1、延长变量的生命周期;
2、限制作用域;
3、修饰的变量只能被初始化1次
4、修饰的变量如果没有初始化,会被自动初始化0
const int a=10; //修饰变量时表示这个为只读(常量化)
int val1=10, val2=20;
const int*p=&val1; //常量化指针,不能通过该指针去修改指向空间的内容
p=&val2 //但是可以修改指向
int const*q=&val1; //同上
int*const po=&val1; //指针的指向不可以发生改变,但是可以通过该指针修改对应空的内容
//指向和内容都不允许修改
const int*const pp=&val1;
void*:
代表任意类型的指针,不支持+、-
自己调自己
voidfun(charch)
{
if(ch<'D'){
printf("%c\n",ch);
fun(ch+1);
printf("%c\n",ch);
}
return;
}
intmain()
{
fun('A');
}
intfun(intn)
{
if
}
不同数据类型的集合
struct 结构体名{
数据类型1 变量名1;
数据类型2 变量名2;
...
数据类型n 变量名n;
};
struct 结构体名 变量名;
struct worker{
char name[20];
int id;
double salary;
};
struct workerboss= {"张三",9527,1.7};
//boss.name = "小红";// err
strcpy(boss.name,"小红");
boss.id=9528'
printf("name:%s\n",boss.name);//s是字符串
struct worker*p=&boss;
p->id=9529;
struct workerarr[10] ={0};
scnaf("%s %d %f",a[0].name,&(arr[0].id),&(arr[0].salary));
共用一个空间
union XXX{
chara;
intb;
};
union xxa;
a.a='A';
printf("%c \n",a.b);//得A
malloc int p = malloc(100sizeof(int))
free(p);
char*fun(char*p)
{
char*q=malloc(100);
strcpy(q,p);
returnq;
}
intmain()
{
char*p="hello world";
char*q=fun(p);
puts(q);
free(q);
}
voidGetMem(char*p)
{
*p=malloc(100);
}
intmain()
{
char*p;
GetMem(p);
strcpy(p ,"hello");
puts(p);
free(p);
} //程序的问题:实参与形参的区别,实参应该是指针的地址
// 地址与值的传递
实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量-->>
在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。-->>
因此应预先用赋值,输入等办法使实参获得确定值
实参和形参的数据传递方式有两种:
一种是值传递,实参和形参都不是指针,这种情况下函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。在函数调用过程中,形参的值发生改变,而实参中的值不会变化。究其原因,是因为这两个参数在内存中位于不同的位置,值传递只是形参将实参的内容复制,在内存中被分配了新的内存单元,在函数执行完毕以后地址会立刻被释放掉,因此形参的改变不会对实参有任何影响。
另一种是地址传递,就是实参与形参共用同一个内存单元,在函数执行的过程中,实际就是对实参的地址进行操作,因此形参改变,实参同步变化,实际他们就是同一变量,因为在内存中占据的就是一个内存单元。
浮点数的存储:
浮点数的二进制:
分成两部分:
整数部分:2的整数次幂相加
小数部分:小数部分不断乘2取整
浮点数在存储时先转化成2进制,再将二进制写成科学计数法-->>1.10010*2^1
浮点数在内存中占4字节32位,最高为表示符号位,其次8位表示指数位,剩下的表示小数位,整数部分少1
void swap(char**sa, char**sb)
{
char*t;
t=*sa;
*sa=*sb;
*sb=t;
}
int main()
{
char*pa="abc";
char*pb="ABC";
swap(&pa, &pb);
}
3.14:
整数部分: 11
小数部分: 0.14*2=0.280.28*2=0.560.56*2=1.120.12*2=0.24...
.0010
故为: 11.0010
浮点数在存储时先转化成2进制,在将二进制写成科学计数法
1.10010*2^1
浮点数在内存中占4字节32位,最高位表示符号其次8位表示指数位剩下的表示小数位
01000000010010001111010111000010
1.10010001111010111000010*2^1
11.0010001111
3.14
8.5:
1000
1
1000.1
1.0001*2^3
01000001000010000 ...
1.0001*2^3