常量就是在程序中不可变化的量
“hello world”
我们习惯于十进制数,通常使用十进制数:如10,20,12等
%d 默认打印是带符号的
%u,表示无符号
int a = 11;
printf("%d\n",a);
一位只能表示 0、1两种状态简称 BIT
一个字节为8个二进制,成为8位,简称BYTE
一个字为两个字节,简称WORD
两个字为双字,简称DWORD
八进制为以8为基数的数制系统,常以0表示八进制:0673;
o: 打印八进制
int a = 11;
printf("%o\n",a);
十六进制以16位基数的数制系统,常用0x表示十六进制:0xAADF;
x:为打印小写字符
X: 打印大写字符
int a = 11;
printf("%x\n",a);
熟悉:存储1个字节(8位),大小的数字(char)
1、用户的数字分为正负数,符号位的存储
2、最高位为符号位,0表示正数,1代表负数
3、1000 0001 左边是高位,右边为低位
//1
+1 : 0000 0001
-1 : 1000 0001
//0
+0 : 0000 0000
-0 : 1000 0000
源码存储导致两个为题:
1、0 有两种存储方式
2、正数和负数相加,结果不正确(计算机只会加,不会减)// 减法 会被执行成 正数 + 负数
// 原码计算
//1-1 = 1 + -1
1: 0000 0001
-1: 1000 0001
1000 0010 = -2
1、正数原码和反码是一样
2、求原码
3、在原码基础上,符号位不变,其他为去反(0变1 、1变0)
// 1
+1 : 0000 0001
-1 : 1111 1110
+0 : 0000 0000
-0 : 1111 1111
反码存储导致1个问题:
1、0 有两种存储方式
//反码计算的方式
// 1 - 1 = 1 + -1
1: 0000 0001
-1: 1111 1110
1111 1111 = -0
通过补码的方式来解决上面的问题:
计算机存储数字以补码的方式存储,(为了解决负数的问题)
1、正数的原码、反码、补码都是一样的;
2、补码为反码+1
//补码:
// 1
+1 : 0000 0001
-1 : 1111 1111
+0 : 0000 0000
-0 : 10000 0000 (最高位丢弃)
以补码来计算
// 1
+1 : 0000 0001
-1 : 1111 1111
10000 0000 = 0
1、提供原码\补码
2、通过原码\补码计算反码
3、通过反码+1 获得补码\原码
// 补码
补码: 1000 0001
反码: 1111 1110
原码: 1111 1111 = -127
十六进制:0x81;
计算机系统中,数值一律用补码来存储主要原因:
1、统一了零的编码
2、符号位和其他为统一处理
3、将减法运算变为加法运算
4、用两个补码表示数相加,如果最高位(符号位有进位),则仅为被舍弃
1、有符号,最高位是符号位,如果1代表负数,如果为0 代表正数
2、无符号,最高位不是符号位,是数的一部分,无符号不可能是负数
%d 默认打印是带符号的
%u,表示无符号
// 等价于 int a =-10
// singed 表示有符号
// unsigned 表是无符号
signed int =-10 ;
数据类型范围:
char 1个字节“
有符号:
正数:0000 0000 -0111 1111 0-127
负数:-0 当做 -128 使用, 要不然就会有两个 0
无符号:0-255
赋值或者运算,记得不要越界:
char a = 127 +2;
// 转换原码
// 129转换成二进制为 1000 0001 这个是负数的补码
printf("%d\n",a);
// 获得结果为 -127
unsigned char b=255+2;
printf("%u\n",a);
// 获得结果为 1
1、sizeof 不是函数,所以不需要包含头文件,他的功能是计算一个数据类型的大小,单位为字节;
2、sizeof的返回值为size_t
3、size_t类型在32位操作系统下是unsigned int 是一个无符号的正数
打印格式 含义 %d 以十进制的方式输出 %o 以八进制的方式输出 %x 以十六进制方式输出,字母小写 %X 以十六进制方式输出,字母大写 %u 输出十进制无符号数
#include
int main(){
int a= 123;// 十进制
int b =0567;//
int c=0xabc;//
printf("a= %d\n",a);
printf("a= %o\n",a);
printf("a= %x\n",a);
printf("b= %d\n"b);
printf("c= %x\n",c);
printf("c= %X\n",c);
scanf("%d",&a);//输入
return 0;
}
数据类型 | 占用空间(字节) | 取值范围 |
---|---|---|
short | 2 | -215-215-1 |
int | 4 | -231-231-1 |
long | 4 | -231-231-1 |
unsigned short | 2 | 0-215-1 |
unsigned int | 4 | 0-231-1 |
unsigned long | 4 | 0-231-1 |
字符型变量用于存储一个单一字符,在c语言中用char表示,其中没个字符变量都会占用1个字节。在给字符变量赋值时,需要用一个对英文半角的单引号,用来包裹字符。
char 本质 就是一个1字节大小的整型。
由来:
1、内存中没有字符,只有数字
2、一个数字对应一个字符,这个规则就是ASCII
3、使用字符或数字给字符变量赋值是等级
4、字符类型本质就是一个1个字节大小的整型
#include
int main(){
char ch = 'a';
printf("ch = %c ,ch =%d\n",ch,ch);
ch=97; //以ASCII 赋值,和ch='a' 等价
printf("ch2 =%c\n" ,ch);
return 0;
}
[root@liu-node1 c_code]# gcc char.c
[root@liu-node1 c_code]# ./a.out
ch = a ,ch =97
ch2 =a
// 验证上面的说法是正确的
小写a 为 97 大写A 65
转义字符,有反斜杠组成的的特殊字符
\n 表示换行
\r 表示句首,并继续打印,前面的字符会被替换
\b 退格键,会删除前面一个字符,然后继续打印
实型变量也称为浮点型变量,浮点型变量是用来存储小数数值。在C语言中,浮点类型分为两种,单精度、双精度
,但是double行变量所表示的浮点数比float型变量更精准。
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15-16位有效数字 |
在使用过程中,尽量使用double变量,数据比较精确
#include
int main(){
float a = 100.9f;
printf("%f\n",a); //100.900002 默认小数点后有6位
double b =1.34;
printf("%lf\n",b); // 1.340000 结果比较正常
return 0;
}
限定符 | 含义 |
---|---|
extern | 声明一个变量,extern声明的变量没有建立存储空间 |
const | 定义一个常量,常量的值不能修改 |
volatile | 防止编译器优化代码 |
register | 定义寄存器变量,提高效率。register是建议型的指令,而不是命令型的指令,如果CPU有空闲的寄存器,那么register就生效,如果没有空闲的寄存器,那么register则无效。 |
#include
int main(){
int a=123;
//%% 在屏幕输出,表示输出一个%
printf("%%d\n");
// 5%,以5个字符输出,没有的字符以空字符填充,默认是右对齐
printf("a ='%5d'\n",a); // ' 123'
printf("a ='%05d'\n",a); // '00123'
printf("a ='%-5d'\n",a); // '123 '表示左对齐
// 零和减号不能同时使用,数据就失效了,没有意义
double b= 3.13;
printf("b ='%lf'\n",b); // 3.130000 自动补零
// 8.3 表示总共有8个字符,3代表小数点后有3位
printf("b ='%8.3lf'\n",b); // ‘ 3.130’ 自动补零
char ch ='a';
putchar(ch);// 把ch 内容输出到屏幕
putchar("\n");
return 0;
}
#include
int main(){
int a;
printf("请输入a:");
scanf("%d",&a);
printf("a = %d\n",a);
int b;
int c;
printf("请输入b,c:");
scanf("%d %d",&b,&c);
printf("b = %d ,c=%d\n",b,c);
return 0;
}
int main(){
char a;
printf("请输入a:");
scanf("%c",&a);
printf("a = %c\n",a);
char b;
printf("请输入b:");
scanf("%c",&b);
printf("b = %c\n",b);
// 二次输出是一个 回车键 ascii 10
return 0;
}
出错的原因: 当用户通过 scanf 输入字符时候,编译器默认把输入的内容放到一块内存中(缓冲区),scanf自动在缓冲区读完内容。
[外链图片转存失败(img-NKZo4mpV-1566437796996)(E:\markdowm\image\1536117682601.png)]
解决方案:
在做字符读取的时候,可以使用getchar();的方式将 "\n"先吃掉,然后在读取。
运算符类型 | 作用 |
---|---|
算术运算 | 处理四则运算 |
赋值运算 | 将表达式的值赋值给变量 |
比较运算 | 表达式的比较,返回一个真或者假 |
逻辑运算符 | 用于根据表达式的值返回真或者假 |
位运算符 | 处理数据的位运算 |
sizeof运算符 | 用于求字节数长度 |
3.2 运算符
其他的语法都是一样的,
两个数相除,想到得到小数,分子分母必须有一个是小数,否则结果只会取整
int a =5 ;
int b =2 ;
double c;
c = a/b; //2.000000
c=a*1.0/b ; // 2.500000
后置++,先用后加
前置++,先加后用
int a=1;
int b=0;
b =a++; //b=1,a=2
a=1;
b=0;
b=++a;// b=2 ,a=2
a=1;
b=0;
a++;
b=a;// b=2 ,a=2
运算符 | 术语 | 实例 | 结果 |
---|---|---|---|
= | 赋值 | a=2;b=3; | a=2;b=3; |
+= | 加等于 | a=0;a+=2 | a=2; |
-= | 减等于 | a=5;a-=2 | a=3; |
*= | 乘等于 | a=2;a*=2 | a=4; |
/= | 除等于 | a=5;a/=2 | a=2; |
%= | 摸等于 | a=5;a%=2 | a=1; |
和java一样,判断真假返回
和java一样,判断真假返回
和java 一样,短路规则
强制类型转换原则,就是小的往大的转,大的往小的转,容易造成错误,或者精度丢失。
double a;
int b =10;
a=b ;// 这里存在隐式的转换,将b 的值转换成double 类型,然后赋值给a
a =(double)1/2; // 这种方式使用的强制类型转换
printf("%lf",a);
// 整型的变量不用%lf打印,除非进行强值类型啊的转换
int c=100;
printf("%lf",(double)c);
c语言支持最基本的三种程序运行结构,顺序结构、选择结构、循环结构
[外链图片转存失败(img-Wo2G598j-1566437796997)(E:\markdowm\image\1536123968342.png)]
#include
int main(){
//if 是一个关键字,条件为真,执行if语句;假不执行
// 非零就是真
if(-1){
//这里的代码会被执行,非零即为真 一定要注意
}
if(1)
{
}
//在if(4==a) 判断语句中,使用常量写在前面的方式可以,防止书写错误,编译不过去
// 所以推荐使用这种写法
int c =4;
if(4 == c){
}else if(4 < c){
}else if(4 > c){
}else{
}
// 在写的情况下注意效率,尽量使用 else if, 不单独使用if;
return 0;
}
同java 不需要深究
int a=10;
int b=20;
int c = a >b ? a : b; // 判断 a和b 大小,如果为真,则c =a ,否则 c=b;
同java 不需要深究
//1、switch 是关键字,没有分号
//2、switch 中值能传入 整型,或者字符型变量
//3、switch 用法类似于电梯模型
int num ;
switch(num){
case 1:
// TODO
break ; //必须有,调出switch ,否则会击穿
case 2:
// TODO
break;
default:
//TODO
break;
}
int num =1;
int sum=0;
while (num<100)
{
sum+=num;
num++;
}
// 无条件先执行一次 循环体
do{
}while();
for(;;){
}
循环嵌套
for(){
for(){
}
}
break 中断出这层循环;
continue 跳过 本次循环,继续循环;
goto 任意地方都可以使用,无条件跳转,建议不要用。
int main(){
goto MIKE;
//todo
MIKE:
//TODO
//这样的话就会将代码 直接跳过,这样的跳转太霸道建议不要用。
// 只能跳转到同一个作用域的 即{}
}
在程序设计中,为了方便处理具有相同类型若干变量按有序形式组织起来,称为数组
数组在内存中**连续的相同类型**的变量空间。同一个数组所有成员都是相同的数据类型,同时所有的成员在内存中的**地址是连续的**。
5.2.1 以为数组定义和使用
//定义一个 数组,
// 定义数组的类型,定义数组的元素个数
// 数组在内存中是连续存储的
// 通过下标访问数组元素,下标从0开始
// 有多少个[] 就代表是几维数组
#define SIZE 10;
int array[10];
// 定义一个的数组名在相同的作用域内,不能和其他的定义相同,会冲突 error
// 在[] 中最好是常量,要不然可能使得其他编译器 编译不过
//定义数组时候,[] 最好是常量,就是固定数组的个数
int a[SIZE];
// 使用数组的时候,下标可以是常量,可以是变量,也可以是表达式
a[0] =1;
int i ;
a[i]=2;
a[i+2]=3;
// 在使用数组的时候,不能越界使用,不然报错,或者编译不过去;
结论: {} 内部的变量或者数组不初始化,就是随机数
int a[10];
for(int i= 0;i<10;i++){
printf("%d\n",a[i])
// 不初始化、就是随机数
// {} 内部的变量或者数组不初始化,就是随机数
}
结论:
1、数组名是常量,
2、为数组首元素的地址
3、sizeof(数组名)测数组总大小
4、元素个数,sizeof(数组名)/sizeof(a[0])
int a[10];
// 数组名为常量, 不能修改
// 数组名是数组首元素地址
printf(" a = %p\n ,&a[0]= %p\n", a , &a[0]);
//
// 求最大值
int a =10;
int b =20;
int c =30 ;
int max ;
//if 的方式就不实现了
//三目运算的方式实现 大小比较
max = ( a > b ? a : b ) > c ? ( a > b ? a : b ) : c;
//求 数组最大值
int aa[] ={1,2,4,6,7,3,1,6,2,76,2,48,5,2}; //定义并初始化数组
int n = sizeof(aa)/sizeof(aa[0]);// 求计算数组的长度
int max=a[0];
for(int i=1;i<n;i++){
if(a[i] > max){
max = a[i];
}
}
printf("数组的最大值为:%d\n",max)
int main(){
int aa[] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; //定义并初始化数组
int n = sizeof(aa)/sizeof(aa[0]);// 求计算数组的长度
int i = 0;
int j = n-1;
int tmp;
while( i < j ){
tmp =aa[i];
aa[i] = aa[j];
aa[j] = tmp;
j--;
i++;
}
for(i = 0;i < n;i++){
printf("%d ,", aa[i]);
}
printf("\n");
return 0;
}
shell 测试:
//通过linux 测试
[root@liu-node1 c_code]# vim arraysort.c
[root@liu-node1 c_code]# gcc arraysort.c
[root@liu-node1 c_code]# ./a.out
15 ,14 ,13 ,12 ,11 ,10 ,9 ,8 ,7 ,6 ,5 ,4 ,3 ,2 ,1 ,
#include
int main(){
int a[] ={1,2,4,6,7,3,1,6,2,76,2,48,5,2}; //定义并初始化数组
int n = sizeof(a)/sizeof(a[0]);// 求计算数组的长度
int i = 0;
int tmp;
int j=0;
printf("排序前 \n ");
for(i = 0;i < n-1;i++)
{
printf(" %d ,", a[i]);
}
printf("\n");
for(i=0;ia[j+1]){
tmp =a[j+1];
a[j+1]=a[j];
a[j]=tmp;
}
}
}
printf("排序后\n");
for(i = 0;i < n-1;i++){
printf(" %d ,", a[i]);
}
printf("\n");
return 0;
}
linux 测试
[root@liu-node1 c_code]# vim arrsort.c
[root@liu-node1 c_code]# gcc arrsort.c
[root@liu-node1 c_code]# ./a.out
排序前
1 , 2 , 4 , 6 , 7 , 3 , 1 , 6 , 2 , 76 , 2 , 48 , 5 ,
排序后
1 , 1 , 2 , 2 , 2 , 2 , 3 , 4 , 5 , 6 , 6 , 7 , 48 ,
1、内存中没有多维数组,都是一维数组,多维数组是一个特殊的以为数组
2、有多少个[] 就是多少维数组
3、二维数组a[m][n],可以理解为m行和n列
第一种方式:
int a[3][4]={
{},
{},
{}
};
第二种方式:
int a1[3][4]={0,1,2,3,4,5,6,7,8,9}
// 如果定义时,同时初始化,第1个[] 可以不写内容。
// 如果第一个[] 不写,必须初始化
int a1[3][4] ={1,2,3,4} //没有初始化的元素赋值为0
// 所有元素初始化为0
int a2[3][4] ={0};
int a[5][10]
// 数组名是常量,不能修改
// sizeof(数组名),测数组的总大小,
printf("sizeof(a) %d",sizeof(a));// 4*5*10 // 测的二维数组 总大小 200
printf("sizeof(a[0]) %d",sizeof(a[0]));// 4*10 // 测的二维数组第0个元素的大小 40
printf("sizeof(a[][]) %d",sizeof(a[0][0]));// 4 // 测的二维数组 第0行 第0列 大小 4
//求行数
int n =sizeof(a)/sizeof(a[0]) ;// 200/40 =5
//求列数
n =sizeof(a[0])/int // 40/4 = 10
//行*列 10*5
1、C语言没有字符串型,用字符数组模拟的。
2,字符串一定是字符数组,字符数组就不一定是字符串。
3、字符数组以字符结尾’\0’,那么这个字符数组就是字符串。
char a[10];
char b[];
//1、常用的初始化,使用字符串初始化,在字符串结尾自动加结束符数字0
//2、这个结束符,用户看不到,但是存在的。通过sizeof 来获取长度可以看到、。
char a7[10]="abc";
sizeof(a7) =4;// 这里默认隐藏了 一个结束符号
//转义字符后\0 后面最好不要加入数字,可能会生成一个新的转义字符
char a[] ={1,2,3}; //3个字符
char B[] ="123";// 4个字符
char a[10] = "123456789"; // 最多只能写9个 要不然,结束符不能插入
char a[100];
scanf("%s",a);//a 没有& ,原因是数组的名称就是地址
printf("%s\n",a);
当调用函数的时候,需要关心5个要素
#include
#include
#include
int main(){
// 设置种子
// 如果种子是一样 ,他随机数参数的值也是一样的
srand((unsigned)time(NULL));
int i = 0;
int num ;
for(i=0;i<10;i++){
num = rand();// 产生随机数
printf("%d\n",num);
}
}
5.5.4.1
char buff[100]={0};
printf("输入你想要的字符:");
scanf("%s",buff);// 不需要取地址&,默认以空格进行分割
printf("%s" ,buff);
// 和getchar 差不多,都是缓冲区作怪
//scanf() 的缺陷,不做越界检查,可能你输入的数据超出边界范围
//gets 已经废弃,也不是安全的,不做安全检查
// 目前主要是用fgets 这个方法
// fgets(char* ,int size,File* stream )
char buf[10];
fgets(buf,sizeof(buf),stdin);
#include
int main(){
char buf[10];
fgets(buf,sizeof(buf),stdin);
//注意换行符也被读取了
printf("%s\n",buf);
return 0;
}
[外链图片转存失败(img-zVzulsxS-1566437796997)(E:\markdowm\image\1536220678204.png)]
puts(char*)
char buf[]="aaabc";
puts(buf);//把buf内容输出到屏幕,会自动换行
stdout //屏幕,代表标准输出
fputs(buf,stdout);
#include
char buf[]="aaabc";
// strlen 需要使用返回值,返回值就是字符串的长度
//从首元素开始到结束符的长度,结束符不算
int len =strlen(buf);
char buf2[]="\0hello";
sizeof(buf2); // 结果为7
strlen (buf2);//0
#include
char src[]="hello mike";
char dst[100];
//功能上src 字符串数组内容 拷贝给dst所代表的数组;
strcpy(dst,src);
//拷贝的时候,遇到\0 结束拷贝
strncpy(dst,src,strlen(src)+1);//使用的时候注意要拷贝字符串长度+1
//可以把\0拷贝过去,但是\0 后面的数据 是不能拷贝的
char src[100] ="121321321313131";
char dst[10];
strcpy(dst,src);
// 出现段错误,就是内存错误,数组越界
比较字符串的每一个字符的ASCII
// int strcmp(char1*,char2*)
// 0 相等
// 大于 >0
// 小于 <0
//int strncmp(s1,s2,num) 比较的是一定范围的数值
字符串内容追加
srtcat(char1,char2);//把char2 追加到char1
srtncat(char1,char2,len);//把char2的n个长度 追加到char1
网络上可以叫做组包,拆包
sprintf(dest,printf); //将打印的内容,输出到目标char数组
fprintf(dest,printf); //将打印的内容,输出到目标文件
sscanf(dest,scanf);//从dest数组中,读取内容 代替屏幕输入
char buf[]="1 2 3";
int a,b,c;
// 注意提取指定格式的内容,char数组的内容和读取内容要一致,如下图所示:
sscanf(buf,"%d %d %d",&a,&b,&c); //读取 buf 值,赋值给 a b c 默认以空格进行分割
//在做字符串输入的时候,默认以空格进行分割,不能使用逗号进行获取。
[外链图片转存失败(img-0Pcl0BOR-1566437796998)(E:\markdowm\image\1536282704601.png)]
char buf[] ="dfadsfdsf";
char *p =strchr(buf,'d');
// 在buf中查询 d ,如果找到就返回d 所在的地址,如果找不到则返回NULL
if(p==NULL){
printf("查询失败!\n")
}else{
printf("p= %s\n",p);
}
// 查询字符串所在的位置的地址,如果查询不到返回NULL
strstr(buf,"ads");
char buf[] ="abc,mike.,jiang,250";
char tmp[100];
strcpy(tmp,buf);
//第一次调用,第一个参数为目标字符串,第二个为切割方式,返回值为字符串数组
// 在匹配字符切割的地方会替换成结束符
// 使用strtok 会破坏原来字符串的结构
// 如果要使用该函数,最好复制一份数组,当做备份
char *p=strtok(buf,",");
printf("%s",p)
char *p1 ;
p1 =strtok(tmp,",");
while(p1!=NULL){
printf("%s",p1);
p1 =strtok(NULL,",");
}
在转换的过程中,需要是数字,或者符号,要不然转换失败,跳过空格遇到\0 结束
atoi 字符串转换成int
atof 字符串转换成float
atol 字符串转换成long
这里还可以用sprintf、sscanf 来格式化字符串,来转换成相应的格式。用%c %d %s 等来操作
c 程序是有函数组成的,我们写的代码都是由主函数main 开始执行的,函数是C 程序的基本模块,
是用于玩成特定任务的程序代码单元。
从函数的定义来看,函数可以分为系统函数和用户自定义函数两种;
函数的作用,减少代码重复,提高代码复用率;
函数可以让程序更加的模块化,从而有利于程序的阅读、修改和完善。
1、函数内部、包含()内部的变量(局部变量),只有在调用时分配空间,调用完毕自动释放;
2、同一个文件不能出现相同的函数名;
3、不同的函数,内部是变量是没有关系;(原因参看第一条)
4、定义函数的参数叫做形参,形参的格式必须为: 类型+ 变量,不能赋值
5、函数没有调用,形参变量不会分配空间,函数调用完毕,行参变量自动释放
6、有返回值的函数,可以不适用返回值。
7、函数的类型和返回类型不一致,以返回类型为准
无参无返回
void functionName(){
}
有参无返回值
无参有返回值
有参有返回值
return 中断函数,程序结束
exit 退出结束整个函数,或者结束进程
函数的声明,告诉编译器,整个函数是有定义的,只是在别的地方;
调用函数前就需要声明,就不会有警告;
函数 只能定义一次,可以声明多次;
函数声明的的方式可以在main 函数前,也可以在函数内部;
声明的函数的形参名称可以和定义的形参不同
声明函数,形参类型一定要写,形参名称可以不写,但是定义的时候,形参名称一定要写
// 声明
int my_strlen(char str[]);
int main(){
return 0;
}
// 此处为定义
int my_strlen(char str[]){
int i = 0 ;
while(str[i] !='\0'){
i++;
}
return i;
}
1、创建工具类函数
2、创建头文件,声明函数
3、创建主函数,包含用户自定义头文件,用户的头文件 用冒号“” 来包裹
4、多文件中,不能出现同名函数定义,static 除外
5、不要再头文件中做定义,分文件系统被多次引入后,会出现同名文件,就类似于java 的导包
6、包含多次只有一次生效 #pragma once
#pragma once // 去重
#ifndef _MYSTRLEN_H
#define
extern int my_strlen(char buf[]);
#endif
[外链图片转存失败(img-FcFzBBXM-1566437796998)(E:\markdowm\image\1536295120152.png)]
1、将内存抽象成一个很大的以为字符数组
2、编码就是对内存的每个字节分配一个32或者64位的编码
3、这个内存编号我们称为 内存地址
内存中没一个数据都会分配相应的地址
存储器:计算机的组成中,用来存储程序和数据的,辅助CPU进行运算处理的重要部分;
内存:内部存储器,暂存程序/ 数据 --掉电丢失 SRAM DRAM DDR DDR2 DDR3
外存:外部存储器,长时间保存程序/数据,掉电不丢失,ROM ERRROM FLASH NAND NOR 硬盘 光盘
内存是沟通CPU 和硬盘的桥梁
存放CPU中的运算数据
暂存与硬盘灯外部存储器交换的数据
[外链图片转存失败(img-OHffgvuf-1566437796998)(E:\markdowm\image\1536296876436.png)]
// 指针也是一种数据类型
// p是一个变量,p的类型为 int *
int * p;
int a =100;
//定义变量时候,* 代表类型,它是指针类型int *
int * p;
p =&a;
// 在使用变量时候,* 代表操作指针所指向的内存
*p =10;
printf("%d",a); //10
int b =11;
p=&b;
*p = 200;
printf("%d %d",a, b); //10,200
int a =10;
int *p =&a;
*p =111; //a=111
int *q;
q=p ;
*q =222;
printf("&a =%p ,p=%p ,q=%p",&a,p,q); // &a &a &a
printf("&a =%d ,p=%d ,q=%d",a,*p,*q);// 222 222 222
1、只有定义后的变量,此变量的地址才是合法的地址;
2、野指针就是保存没有意义的地址的指针变量;
3、操作也指针变量本身不会有任何问题;
4、操作野指针所指向的内存才导致错误。
//1、空指针 就是给指针变量赋值为NULL
int *p =NULL;
int a =111;
p = &a;
if(p!=NULL){
*P = 20;
}
// 这样就可以操作a 变量的内存,a 变量的值变成了20
// 32位编译器32位大小(4字节)保存地址
// 64位编译器64位大小(8字节)保存地址
int a =sizeof(int *);
int b =sizeof(char *);
int c sizeof(double *);
printf("a =%d,b=%d,c=%d",a,b,c);
[root@liu-node1 c_code]# vim ch.c
[root@liu-node1 c_code]# gcc ch.c
[root@liu-node1 c_code]# ./a.out
a =8,b=8,c=8
[root@liu-node1 c_code]#
//若果定义一个合适类型的变量保存另一个变量的地址
// 如果需要保存变量地址的类型就在基础上一个*
int a =10;
int *p =&a;
int **q =&p;
int ***m =&q;
[外链图片转存失败(img-pE0uhPsf-1566437796999)(E:\markdowm\image\1536304642069.png)]
[外链图片转存失败(img-VrY7zNzx-1566437796999)(E:\markdowm\image\1536305074507.png)]
[外链图片转存失败(img-MepSPsYb-1566437797000)(E:\markdowm\image\1536305637672.png)]
1、不可以定义void 类型的普通变量,不能确定类型
2、可以定义 void * 变量, void * 也叫做万能指针
3、void * 可以指向任何类型的变量,使用指针所指向的内存时,最好转换成他本身的指针类型
void * p=NULL;
int a = 10 ;
p =&a;
*((int *)p) =222;
printf("*p %d",*((int *)p)) // 222
[外链图片转存失败(img-L8NghDQA-1566437797000)(E:\markdowm\image\1536309179725.png)]
总结:指针的加法,不是传统的加法
步长有指针指向的类型类决定。
指针数组:他是数组,每一个元素都是指针。
数组指针:他是指针,指向数组的指针。
无论什么类型,只要是变量传递,就是值传递;
如果通过函数改变实参,必须地址传递
[外链图片转存失败(img-JXyRyCTF-1566437797001)(E:\markdowm\image\1536539568255.png)]
形参中的数组,不是数组,他是普通的指针变量
参数数组:int[10000],int[],int *a 对编译器而言,没有任何区别,编译器都当做int * 处理
形参中的数组和非形参数组的区别:形参中的数组是指针变量,非形参数组就是数组。
总结:在传递过去方法体重的,数组的长度要传递过去。要不然无法计算出数组的长度和个数,操作就无法完成。
void print_array(int *a){
int i = 0;
// 在64位系统中,a是指针变量,长度为8
// a[0] 是int 类型,是4 最重结果为2
int n = sizeof(a)/sizeof(a[0]);
a =NULL;
//形参中的数组,不是数组,它是普通的指针变量
}