指针即为地址,指针几个字节跟语言无关,而是跟系统的寻址能力有关。譬如以前是16为地址,指针即为2个字节,现在一般是32位系统,所以是4个字节,以后64位,则就为8个字节。
指针运算符:
1.算数运算:指针±整数; 加就是右移一个类型长度字节,反之亦然,int就移动4个字节,char就移动1个字节。
2.指针运算:指针-指针=内存中的距离,该距离的单位是类型长度单位;并且必须相对于同一数组而言
3.关系运算:指针≥ ≤ >< = 指针,必须相对于同一数组而言
1.整型
整型初始化 int a1 = 100;
负数初始化 int a2 = -2;
指针赋值 int *a3 = &a1; note:这种方式会让人感觉是把a1的地址传给了 *a3,其实是传的值是a3,好一点的写法,int *a3=NULL; a3 = &a1;
数组初始化 int a3[5] = {0} ; int a3[5] = {1,2,3,4,5};其中的a3是数组a3的起始地址;printf("a3 = %p",a3)可以打出a3的地址,用*a3可以打出数组a3第一个值a3[0].printf("a3=%p", &a3)的值就是a3的地址
对于 int a4[5] = {1,2,3}, 实际长度比赋值的数目多,对于没有初始化的位置,则默认初始化为0,对于 int a5[] = {1,2,3,4};能自动的分配数组长度,这种方式比较可靠
注意a3+3,不是a3的地址右移动3个字节,而是3个int长度的位置。实际是到a[4]这个地址
二重指针 int **a4 = &a3, 把指针a3的地址取出来,赋值给a4, **表示变量 a4里面存的是一个地址,这个地址指向的内容,又是另一个地址,所以 *a4是a3地址里面的值。
2.字符,字符数组,字符指针,字符串
1.字符
char在内存中占一个字节,8个比特位,无符号的范围是0-255,unsigned char 范围-127~127
字符初始化:char a = 'a'; ,赋值:a= 'c'; 同样可以 &a取出其地址。
2.字符数组
字符数组初始化:char temp[6] = {0},temp的6个位置都填'\0'字符; char temp[] = {'a', 'b', 'c', 'd'};char temp[5] = {'a', 'b', 'c', 'd'}或者 这种方式较冗余,对于 char temp[10] = {'a', 'b', 'c', 'd'},则实际只用了4位,打印出来还是abcd.
char temp1[]="abcd";这种方式比较方便,也能动态分配数组长度。
字符数组的整体赋值只能在字符数组初始化时使用,不能用于字符数组的赋值,字符数组的赋值只能对其元素逐个赋值
char str[ ];
str="I am happy";//错误,字符数组的赋值只能按元素一一赋值
用两种不同方法初始化字符数组后得到的数组长度是不同的
#include <stdio.h>
void main(void)
{
char c1[]={'I',' ','a','m',' ','h','a','p','p','y'};
char c2[]="I am happy"; //后面会添加/0所以长度是11
char C3[100] = "abcde"; //以最长的长度为准
int i1=sizeof(c1);
int i2=sizeof(c2);
int i3=sizeof(c3);
printf("%d\n",i1);
printf("%d\n",i2);
printf("%d\n",i3);
}
结果:10, 11 , 100
字符数组本质也是数组,因此可以适用: 算数运算,指针运算,关系运算。
strcpy(char *dst, char *src);也可以传入 temp和 temp1因为这两个也是地址。
3.字符指针
字符指针初始化:char *a = NULL; char b* = "abcd";
字符指针赋值:a= "12345" , b= "hello", 可以通过指针指向字符串的方式进行指针赋值。
注意字符数组不能这样赋值:char m[8] = {0}; m = "12345678";
4.结构体
1.结构体定义
struct test {
char[8] name;
int age;
};
typedef struct test TEST;
则声明就只用,TEST t5;
2.结构体声明和初始化
struct test t1; //还未初始化,只是声明
strcpy(t1.name, "abc"); //赋值
t1.age = 10; //赋值
结构体初始化:
不指定初始化的项目
struct test t2 = {
"abc",
100
};
指定初始化的项目
struct test t3 ={
.name = "ABC",
.age = 100
};
简化声明:
struct book{
char name[8];
char auth[8];
} book1;直接简化声明book1,能够使用了。
可以用
typedef strcuct class{
int class_num;
char member[56];
} CLASS;
这样就可以简洁方便的定义结构体 CLASS,直接
CLASS class1; 等价于 struct class class1。
3.结构体指针
struct book *b; //声明
b = &book1; //赋值
printf("b->name is %s", b->name); //指针取值要用 -> 符号
printf("b->age is %s", b->auth);
1.结构体嵌套
struct student{
char name[8];
int age;
};
struct class{
int num;
struct student st1;
struct strudent *t2;
} cc1;
对于st1的取值,应该是,cc1.st1.age 和cc1.st1.name
对于st2的取值,应该是,cc1.st2->age 和cc1.st2->name;
因为 t2是指针,所以要用 ->取值。对于用 . 还是 -> 要看当前结构体的类型是否是指针。比如cc1不是指针,所以用 . 取值,而st2是指针,则要用 -> 。
2.结构体数组
6.malloc和free
1.malloc()
malloc()找到可用内存中一个大小适合的块。
malloc分配的内存是匿名的,也就是说,malloc()分配了内存,但没有为它指定名字。
然而,它却可以返回那块内存第一个字节的地址。
因此,可以把那个地址赋值给一个指针变量,并使用该指针来访问那块内存。
因为char代表一个字节,所以传统上曾将malloc()定义为指向char的指针类型。
然而,ANSIC标准使用了一个新类型:指向void的指针。这一类型被用作“通用指针”。
函数malloc()可用来返回数组指针、结构指针等等,因此一般需要把返回值的类型指派为适当的类型。
在ANSIC中,为了程序清晰应对指针进行类型指派,但将void 指针值赋值给其他类型的指针并不构成类型冲突。
如果malloc()找不到所需的空间,它将返回空指针。
我们使用malloc()来创建一个 数组。可以在程序运行时使用malloc()请求一个存储块,另外还需要一个指针来存放该块在内存中的位置。
double * ptd;
ptd = (double * ) malloc (30 * sizeof(double));
这段代码请求30个double类型值的空间,并且把ptd指向该空间所在位置。
此时可以将指针转换成数组,ptd[0],ptd[1].....ptd[29]分别对应每个元素
记住:数组的名字是它第一个元素的地址。
因此,如果令ptd指向一个内存块的第一个元素,就可以像使用数组名一样使用它。
也就是说,可以使用表达式ptd[0]来访问内存块的第一个元素,pd[1]来访问第二个元素,依此类推。
正如前面所学,可以在指针符号中使用数组名,也可以在数组符号中使用指针。
使用第二种或第三种方法可以做一些用普通的数组声明做不到的事:
创建一个动态数组(dynamic array),即一个在程序运行时才分配内存并可在程序运行时选择大小的数组。
例如,假定n是一个整数量。在C99之前,不能这样做:
double item[n]:/*如果n是一个变量,C99之前不允许这样做*/
然而,即使在C99之前的编译器中,也可以这样做:
ptd =(double*)malloc(n*sizeof(double));/*可以*/
这行得通,而且正如您将看到的那样,这样做比使用一个变长数组更灵活。
一般地,对应每个malloc()调用,应该调用一次free()。
函数free()的参数是先前malloc()返问的地址,它释放先前分配的内存。
这样,所分配内存的持续时间从调用malloc()分配内存开始,到调用free()释放内存以供再使用为止。
设想malloc()和free()管理着一个内存池。
每次调用malloc()分配内存给程序使用,每次调用free()将内存归还到池中,使内存可被再次使用。
注意:free()的参数应是一指针,指向由malloc()分配的内存块;
其他方式(例如声明一个数组)分配的内存是不能使用free()去释放的。
malloc结构体举例
struct student
{
int num;
char name[8];
};
struct student *s1 = NULL; /*注意写法*/
s1 = (struct student*)malloc(sizeof(struct student)); /*注意写法*/
2.free ( )
在编译程序时,静态变量的数量是固定的:在程序运行时也不改变。
自动变量使用的内存数量在程序执行时自动增加或者减少。
但被分配的内存所使用内存数量只会增加,除非您记得使用free()。
例如,假定有一个如下代码勾勒出的函数,它创建一个数组的临时拷贝:
#include<stdio.h>
#include<malloc.h>
void gobble (double ar[], int n);
int main(){
double glad[2000];
int i;
for(i = 0; i<100000000; i++){
gobble(glad, 2000);
}
}
void gobble(double ar[], int n){
double *temp = (double *) malloc(n*sizeof(double)) ;
// free(temp);/*忘记使用*/
}
第一次调用gobble()时,它创建了指针temp,并使用malloc()为之分配16000字节的内存(设double是8个字节)。
假定我们如暗示的那样没有使用free()。
当函数终止时,指针temp作为一个自动变量消失了。
但它所指向的16000个字节的内存仍旧存在。
我们无法访问这些内存,因为地址不见了。
由于没有调用free(),不可以再使用它了。
第二次调用gobble(),它又创建了一个temp,再次使用malloc()分配16000个字节的内存。
第一个16000字节的块已不可用,因此malloc()不得不再找一个l6000字节的块。
当函数终止时,这个内存块也无法访问,不可再利用。
但循环执行了1000次,因此在循环最终结束时,已经有1600万字节的内存从内存池中移走。
事实上,在到达这一步前,程序很可能已经内存溢出了。
这类问题被称为内存泄漏(memory leak),可以通过在函数末尾处调用free()防止该问题出现。