C语言指针是C语言非常重要的一个概念,我们可以通过指针去读取内存的数据。
声明方式:
type *Ptr;
type 可以是任何可以用类型。
虽然我们通过 int *Ptr,声明了一个指向int类型的指针,但是指针的占用的实际内存,并不与int相同。
#include <stdio.h>
int main(void)
{
int arr[][5] = {10, 20, 30, 40, 50, 60, 70};
int *ptr;
printf("array is using %ld bytes \n", sizeof(arr));
printf("int uses %ld bytes\n", sizeof(arr));
printf("Ponit uses %ld bytes \n", sizeof(arr));
return 0;
}
指针的初始化
int *Ptr, a;
Ptr = &a;
&a的意思就是取到变量a的地址,Ptr = &a,就是把指针的值改为a的地址,*Ptr就是取a的值。
另外,地址操作符&,不能用在常数(&20),表达式(&(a+b)),或者是register类型的变量,和位领域。它只能应用在内存的一个对象,比如变量,数组和函数。
空指针
当指针被声明后,它就会给出一个任意值。事实上,这个值是内存当中的垃圾,最好办法就是将ptr的值变为NULL。NULL是一个宏,定义在stdio.h文件中。虽然我们也可以给ptr赋值0,但是最好还是通过这个NULL宏定义来设置空指针。
#include <stdio.h>
int main(void)
{
int *ptr;
printf("Addr = %p\n", ptr);
ptr = NULL;
printf("Addr = %p\n", ptr);
return 0;
}
结果:
Addr = 0x7ffd7ea4be70
Addr = (nil)
使用一个指针
int *Ptr,a;
a = 10;
Ptr = &a;
Ptr表示的是内存的地址,*Ptr意思是读取这个内存地址内存放的数据。
当然比较复杂的还有**Ptr,同理,*Ptr的意思这个内存的存放的内容,这里面可以是int float dobule,char…但是这个是个特列,*Ptr存放的又是一个指针,**Ptr的意思就是,在一个内存地址里面存放了一个指针,然后通过这个指针再找到另外一个地址,然后取这个内存里的数据。
值得注意的是 ,在它被废弃前,指针的值必须指向一个有效内存地址,例如一个变量的地址。
在Unix/Linux中通常,当我们的指针错误引用,就会跳出一个“Segmentation fault”错误。
所以,一定要记住在使用指针前,一定要初始化它。
void* Pointer
一个void类型的指针一个可以指向所有类型的广谱指针。任何的指针都可以投向 void* 和返回普通类型,并且在这个过程中,信息不会有任何的丢失。
使用const限定符
为了防止一个指针的指向的值发生变化,我们可以在它的类型前面加上const关键字。
int j,i =10;
const int *ptr;
ptr = &i; //合法
*ptr = 30; //非法
ptr =&j; //合法
为了放置指针的变量存放的地址发生变化,我们会在类型后面加上const,但是同时必须在声明的同时进行赋值。
int i, j;
int* const ptr = &i;
ptr = &j; //非法
*ptr = 30 //合法
指针运算
我们通过指针的加减运算来指向同一个数组的不同元素。
指针和整数
ptr = ptr + n;
如果ptr指向arr[i],n是3,则ptr指向的元素是arr[i+3]
指针数组
这是一个存储指针类型的数组,声明如下:
int *p[3];
这个数组是由3个指向int类型的指针组成。
int *p[3],i=100,j=200,k=300;
p[0] = &i;
p[1] = &j;
p[2] = &k;
二维数组:
char names[][12] = {“First name”, “Second name”, “Third”};
我们声明了一个二维数组,每一行占用12个bytes
如果我们使用指针数组的话:
char *name[] = {“First name”, “Second name”, “Third”};
数组就会自动分配了相对应的内存,这样就会节省内存。
使用指针除了可以节省内存外,还可以提高程序的性能。
指针与二维数组
C语言只有一维数组,二维数组其实也是一维数组的变形。
我们声明一个二维数组:
int a[2][3];
在这个数组中一共是6个地址,我们可以通过指向第一个元素的指针,依次遍历出所有元素的值。
例如:
#include <stdio.h>
int main(void)
{
int i;
int a[2][3] = {1,2,3,4,5,6};
int *ptr;
ptr = a[0] ;
for(i=0;i<6;i++){
printf("The a[%d] = %d\n",i,*(ptr+i));
}
}
函数的指针
当一个函数被定义,编译器就会为之分配内存用来存储他的代码。因此,每一个函数都会像普通变量一样会有一个地址。函数的指针指向这个存储函数代码的内存地址。C处理函数指针跟处理其他普通指针一样。例如,我们可以存储他们在一个变量或者像数组元素一样使用它们,声明的语法如下:
return_type (*pointer_name) (type_param_1 name_1, type_param_2 name_2,
…, type_param_n name_n);
int (*ptr)(int arr[], int size);
ptr被声明为一个指向函数的指针,这个函数接受一个int类型的数组,和一个int变量,最后返回值是一个int类型。
void (*ptr)(double *arr[]);
ptr是指向函数的指针,此函数有一个参数,这个参数是一个double类型的指针数组,此函数没有返回值。
int test(void (*ptr)(int a));
一个名为test的函数,返回值是int。test函数的参数是一个没有返回值的函数,此函数的参数是一个int的变量。
#include <stdio.h>
void test(int a);
int main(void)
{
void (*ptr)(int a);
ptr = test;
ptr(10);
return 0;
}
void test(int a)
{
printf("The double a is %d\n", a * 2);
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void make_rand(void (*ptr)(int evt, int num)); /* Although we could write
make_rand(void ptr(int evt, int num));, we prefer the pointer
declaration. */
void handle_event(int evt, int num);
int main(void)
{
srand(time(NULL));
make_rand(handle_event);
return 0;
}
void make_rand(void (*ptr)(int evt, int num))
{
int i, cnt;
cnt = 0;
while(1){
i = rand();
cnt++;
if(i >= 1000 && i <= 2000){
(*ptr)(cnt, i);
return;
}
}
}
void handle_event(int evt, int num)
{
printf("Times:%d Num:%d\n", evt, num);
}
函数指针的数组
通俗的讲就是一个数组,这个数组的元素都是指针,这些指针都指向函数
#include <stdio.h>
int test_1(int a, int b);
int test_2(int a, int b);
int test_3(int a, int b);
int main(void)
{
int (*ptr[3])(int a, int b);
int i, j, k;
ptr[0] = test_1; /* ptr[0] points to the memory address of
test_1(). */
ptr[1] = test_2;
ptr[2] = test_3;
printf("Enter numbers: ");
scanf("%d%d", &i, &j);
if(i > 0 && i < 10)
k = ptr[0](i, j); /* Call the function that ptr[0] points to. We could also write k = (*ptr[0])(i, j). */
else if(i >= 10 && i < 20)
k = ptr[1](i, j); /* Call the function that ptr[1] points to. */
else
k = ptr[2](i, j); /* Call the function that ptr[2] points to. */
printf("Val = %d\n", k);
return 0;
}
int test_1(int a, int b)
{
return a+b;
}
int test_2(int a, int b)
{
return a-b;
}
int test_3(int a, int b)
{
return a*b;
}