本节结束。==================================================================
数组类型:是一个复杂类型;(由数组元素类型和数组长度综合表述)如下;数组类型是:(int []);
类型的本质是:固定大小内存块的别名。是模具。
一. 对一维数组 C语言规定: int c[10];
1.一维数组名:c是数组首元素的地址,c+1步长为 4个字节,一个 int 元素大小。
&c是整个数组的地址,&c+1步长为40个字节,十个 int 元素大小
虽然c和&c的值相同,但是他们所代表的步长不同。
2.数组第i 个元素这样表示:c[i] 或 *(c+i); 给它赋值。c[i] = 5 或 *(c+i) = 5;
本节结束。==================================================================
二. 多维数组(int a[3][4])
1.多维数组名:a是指向一个一维数组 的数组指针,a 的步长为一维的长度(即每次+1都跳一行)(16个字节,四个int元素元素大小)
2.(a+i)代表是整个第i行的地址,(代表二级指针)
*(a+i)代表第i行首元素地址 (代表一级指针)
*(a+i)+j或(&a[i][j])代表第i行第j个元素的地址
*(*(a+i)+j)或(a[i][j])代表第i行第j个元素的值。
本节结束。==================================================================
三.多维数组做函数参数的退化问题
"数组做函数参数时会退化为指针" 如下代码:sizeof(a) == 4; sizeof(b) == 4;都是一个指针大小,并不是数组大小。
"只不过每种退化后的数组(为指针)步长可能不同"。
所有的数据在内存里面都是线性存储的。包括多维数组
1.一维数组的实参传递(实参把值传给形参):
int fun(char a[20],int n);(fun是被调函数)
int fun2(char b[3][4]);
sizeof(a)的是4个字节; sizeof(a)/sizeof(*a)的是4(因为*a是char型元素,一个字节);
1)高效
2)C语言处理a[n]的时候(形参),它没有办法知道n是几,它只知道&a[n](即a的地址)是多少,它的值作为参数传递进去了.
2. 等价关系:
数组参数 等效的指针参数
1)一维数组 char a[30] 指针 char *
2) 指针数组 char *a[30] 指针的指针 char **a
3) 二维数组 char a[10][30] 数组的指针 char (*a)[30];
本节结束。===================================================================
四.指针的杂点:
1."指针的本质:指向内存的地址", "没有内存就没有内存地址,没有内存地址就没有指针"。
2."间接赋值是指针存在的最大意义"。
3
1.数据类型的本质:占固定内存大小的别名。 类型就是模具,变量是模具产生的实物
2.变量的本质:(一段连续)内存空间的别名(是一个门牌号)
3.数组的内容:如 arr[5]; arr数组空间的首元素的地址, &arr代表的是整个数组的首地址,
arr+1;数组第二个元素的地址,向下移动四个字节。
&arr+1;代表向下移动20个字节,也就是一个数组内存空间大小。 &arr和arr的值相同,但 +1 的结果不同,因为他们俩本质上是数据类型不同
4.指针指向谁,就把谁的地址赋给指针
5.指针变量和它所指向的内存空间变量是两个不同的概念("不懂")
6.注意: return不是把内存块 64个字节给return出来,而是把内存块的首地址(内存块标号0X23124)给return出来。
(如下代码:17行和27行输出的值相同)
#include <stdio.h>
#include <string.h>
char *func1()
{
char arr[10];
strcpy(arr,"sdqwyg");
printf("arr:%s
",arr);
printf("&arr:%p
",arr);
return arr;
}
int main(void)
{
char *ch = NULL;
printf("&ch:%p
",ch);
ch = func1();
printf("ch:%s
",ch);
printf("&ch:%p
",ch);
return 0;
}
C提高上课第一天===============================
1.结构体字节对齐原因:用空间换时间。
2.操作系统最主要的作用:资源分配。
3.句柄:结构体变量
4.野指针的特点:操作野指针本身变量没有任何问题;但是不能操作野指针指向的内存空间
即可以给指针变量(int *p) p 赋任何地址。如(p = NULL; p = 0xaabb; p = 0x9988;)
但是不能操作野指针指向的内存空间(大部分情况都是如此)。(*p = 100;*p = "sdb";这种情况不可以)。
5.冒泡:for(int i = 0; i < n-1; i++)
for(j = 0;j < n-1-i; j++)
if(a[j] <a[j+1])
6.选择:for(int i = 0; i <n-1; i++ )
for(j = i+ 1; j< n; j++)
if(a[i] <a[j])
7.实参数组名,就是数组,sizeof(数组名)就是数组总大小
形参数组名,就是指针。sizeof(数组名)就是指针大小(每个操作平台大小有区别)。
C提高第二天=======================================
1.通过指针 操作指针指向的内存,两个要素
1)"首地址";2)"步长(由指针指向的空间决定)"
"指针最重要的意义在于间接赋值"
2.如果是值传递,(即变量本身传递),形参的任何修改不会影响到实参("数组名除外,因为数组名是数组的首地址")
形参(被调函数里面的变量);实参(主调函数里面的变量)
3.如果是址传递,(即变量地址传递),形参的任何修改会影响到实参
1)地址传递, 2)传参过程中建立形参和实参的指针指向关系 3)被调函数内部通过*间接赋值
4.指针做函数参数:有输入和输出特性
1)输入特性:实参的内存已确定,在主调函数分配内存
2)输出特性:在被调函数分配空间
5.字符数组初始化:(三种情况)
1)char str1[] = {'a','b','c','d'};
printf("str1: %s
",str1); //打印出abcd+乱码,因为数组中没有结束符' ';
2)char str2[100] = {'a','b','c','d'};
printf("str2: %s
",str2); //打印出abcd;因为这相当于数组部分初始化,有结束符' '
6.通过[]和通过*效果是等价的,都是操作指针所指想的内存,
int a = 10; int *p = &a; printf("%d, %d
",*p, p[0]);打印效果是一样的
C提高第三天======================================================
1. 普通数组(char p[4]);是一个数组,元素是字符。
指针数组(char *p[4]);是一个数组,元素是指针。
他们俩的传参:1)"传递":普通数组:func(p,len) "接收" void func(char *p,int len );或者 void func(char p[],int len ); 或者 void func(char p[4],int len );
2)"传递" 指针数组: func(p,len) "接收" void func(char **p,int len);或者 void func(char *p[],int len); 或者 void func(char *p[4],int len);
当不确定形参类型时,如果是"值传递":可以把实参的原型放上去,再进行优化或不优化都对。如"指针数组"
如果是"址传递";需要在实参原型上在加上一个 "*" 号,再进行优化活不优化都对。
2.指针与数组如果做普通变量;两者有本质有区别。
指针与数组如果做形参时,两者是等价的。
3.sprintf的用法: char buf[100];格式化一个字符串,中间带数字0:
1)字符串自带结束符(不是通过格式化给的); sprintf(buf,"sadd dada sads ");然后打印内容:printf("%s
",buf);内容为"sadd";
2)格式化一个字符串,中间带数字0,以%d匹配;因为占四个字节,结束符(' '或'0')占一个字节;
sprintf(buf,"sadd%ddada%dsads%d",0,0,0);printf("%s
",buf); 内容为"sadd0dada0sads0"
3)格式化一个字符串,中间带数字0,以%c匹配;sprintf(buf,"sadd%cada%csads%c",0,0,0);
printf("%s
",buf);
printf("%s
",buf);
int len = sprintf(buf,"sadd%cada%csads%c",0,0,0);它的返回值很重要:是整个字符串的长度;
C提高第四天=================================
1.异或(相同为0,不同为1)的特点:1)自己和自己异或为0;2)一个数每一位和0异或:维持不变
3)一个数每一位和1异或:取反
2.左移<<:(左边丢弃,右边补0)。左移n位("无越界情况下"),相当于原数乘以2的n的次方
右移>>:(右边丢弃,左边补0)。
3.掩码:通过连续的0和1组成,0X0000110,
如(0X00ff00),通过掩码可以把某些部位的值改变。
4.一维数组名:int a[10]; printf("a:%d,a+1:%d
",a,a+1);
printf("&a:%d,&a+1:%d
",&a,&a+1); 两种打印情况不同
虽然a和&a的值一样,但他们的数组类型是不一样。
a:代表首元素地址,第0个元素地址,&a[0],第0个元素是int, +1,就是+4个字节。
&a:代表整个一维数组的地址,整个数组的长度;
数组类型
5."指针数组":是一个数组,每个元素都是指针。 int *p[10];
"数组指针":是一个指针,指向数组的指针。 int (*p)[10];
有"typedef"是类型,没有"typedef"是变量
定义数组指针的三种方式:
1)先定义数组类型,再根据数组类型定义指针变量(不常用)
int a[10] ;typedef int ARR[10]; ARR *p1 = &a;
p1, &a首行地址;首行地址转首元素地址:*p1;
第i个元素的地址*p1 +i; 取第i个元素的内容:*(*p1+i);
2)直接定义数组指针变量(常用)
int a[10]; int (*p)[10]; p = &a;
3)定义数组指针类型,再根据类型定义变量(常用)
int a[10]; typedef int (*ARR)[10]; ARR p; p = &a;
6.多维数组
int a[3][4];
1)多维数组名"a",代表第0个元素的地址(相当于整个多维数组首元素地址或首行元素地址)
"a+1"跳四个元素(int[4]),16个字节。
2)多维数组名取址"&a",代表整个数组的地址。
"&a+1"跳整个数组大小(int [3][4]),48个字节 。
sizeof(a) == 48; sizeof(a[0]) == 16; sizeof(a[0][0]) = 4;
3)多维数组名就相当于数组指针;如上;( int (*p)[4] = a 或 int (*q)[4] = NULL, q = a);
4)特殊情况,当(数组指针)与(多维数组的数组名的步长)不同时;
""
5)"二级指针与二维数组不是同一种类型";
C提高第五天=================================================================
"定义结构体变量时,要把定义语句写在函数的定义区,如果写在其他地方,由于编译器的问题,会导致定义变量不成功,编译器不认识"。
"结构体变量之间可以进行直接赋值,但是需要注意深拷贝和浅拷贝问题"(下面有讲解)
1.结构体:
Stu *s1 = NULL;
Stu p = { 20, "nihao","sada" };
s1 = &p; //这里的注意写法,给结构体指针变量整体赋值。(不能写成 *s1 = p ,原因 *s1是已经开始取结构体的内容,因为s1没有赋值,相当于野指针,不能取值);
2.结构体的名是一个普通的变量(和 int a 中的 a 无区别);
3.结构体中给指针的赋值操作和数组的赋值操作。(特别注意)
ypedef struct Stu
{
int age;
char name[20];
char *p;
char *size;
}Stu;
int main07(void)
{
Stu *s1 = NULL;
Stu p = { 20, "nihao","sada" };
s1 = &p; //这里的注意写法,给结构体指针变量整体赋值。(不能写成 *s1 = p ,原因 *s1是已经开始取结构体的内容,因为s1没有赋值,相当于野指针,不能取值);
printf("%d, %s, %s ", s1->age, s1->name, s1->p);
printf("
");
system("pause");
return 0;
}