一、指针(用于存储变量的地址)
一元 & 运算符给出变量的存储地址。
注:&地址运算符 e.g. &a表示变量a的地址。
e.g.p=11;
printf ("%d %p\n",p,&p);
注:%p 是输出地址的转换说明。
二、间接运算符:*
e.g. a=&b; c=*a; 等价于 c=b;
三、声明指针
e.g. int *pi; float *p , *m; char *n;
(*)表明声明的变量是一个指针。声明指针变量时必须指定指针所指向的变量的类型。why?Because 不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。
四、初始化指针
在C语言中,可以通过两种方式来初始化指针:
1. 空指针初始化:将指针变量赋值为NULL,表示该指针不指向任何有效的内存地址。可以使用以下语法进行空指针初始化:
```
int *ptr = NULL;
```
或者
```
int *ptr;
ptr = NULL;
```
注意:空指针不指向有效的内存地址,因此在使用指针之前,应该确保它不是空指针,否则可能会导致程序崩溃或未定义的行为。
2. 指向有效内存地址的指针初始化:可以将指针指向一个已经存在的变量或者动态分配的内存块。以下是两种常见的初始化方式:
a. 指向已经存在的变量:
```
int num = 10;
int *ptr = #
```
或者
```
int num = 10;
int *ptr;
ptr = #
```
b. 动态分配内存块:
```
int *ptr = malloc(sizeof(int));
```
注意:在使用动态分配的内存之后,应该使用`free()`函数释放它,以避免内存泄漏。
五、两个例子
date + 2 == &date [ 2 ]; //相同的地址
* (date+2)== date [ 2 ]; //相同的值
易混淆: *(date + 2) //date第三个元素的值
*date + 2 // date 第一个元素的值加二
六、函数、数组、指针
例、定义一个函数返回数组中所有元素之和。
int sum(int *ar, int n)
{
int i;
int total = 0;
for ( i = 0; i<n;i++)
total+= ar[i];
return 0;
}
注:数组名是该数组首元素的地址。e.g. ar=&ar [ 0 ];
其中,int *ar 等价于 int ar [ ]
#include <stdio.h>
#define SIZE 10
int sum(int ar[], int n);
int main(void)
{
int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
long answer;
answer = sum(marbles, SIZE);
printf("The total number of marbles is %ld.\n", answer);
printf("The size of marbles is %zd bytes.\n",
sizeof marbles);
return 0;
}
int sum(int *ar, int n) // how big an array?
{
int i;
int total = 0;
for( i = 0; i < n; i++)
total += ar[i];
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
等价于
#include <stdio.h>
#define SIZE 10
int sump(int * start, int * end);
int main(void)
{
int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
long answer;
answer = sump(marbles, marbles + SIZE);
printf("The total number of marbles is %ld.\n", answer);
return 0;
}
/* use pointer arithmetic */
int sump(int * start, int * end)
{
int total = 0;
while (start < end)
{
total += *start; // add value to total
start++; // advance pointer to next element
}
return total;
}
七、指针操作
1.指针赋值:用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。
2.解引用:*运算符给出指针指向地址上存储的值。
3.指针与整数相加:整数会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。
e.g. ptr1 + 4 等价于 & urn [ 4 ] //其中,ptr1 是指向urn [ 0 ] 的指针
4.递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。
5.指针减去一个整数:指针必须是第一个运算对象,整数是第二个运算对象。该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。
e.g. ptr 3 - 2 等价于 & u[2] //其中,ptr 3 是指向u [ 4 ] 的指针
6. 递减指针:同递增指针。
7.指针求差:用于计算两个指针的差值,求差的两个指针分别指向同一个数组的不同元素,可以用于计算两元素之间的距离。差值的单位与数组类型的单位相同。 int ptr 2 - int ptr 1 = 2 , 2为int类型,一个int占4字节,故总的值会差4*2=8。
8.比较运算:(即:比较指针所指的地址的比较)
八、指针的基本用法
第一,在被调函数中改变主调函数的变量;
第二,保护数组中的数据。
九、用 const 保护数组的数据不被修改
e.g. int sum ( const int ar[ ] , int n );
如果编写的函数需要修改数组,在声明数组形参时,则不使用const;反之,用const。
十、const的用法
const创建变量、const创建数组、const创建指针和指向const的指针。
注:
int i
const int * p1= &i;
int const *p2= &2; //星号在后表示指针所指向的值不能被修改
int * const p3= &3; //星号在前表示指针不能被修改
十一、指针和多维数组
假设:int demo [4] [2];
1、demo[0] 是一个占用一个int大小对象的地址,而demo是一个占用两个int大小对象的地址。
但是,他们两个的值相同。
2、**demo 与*&demo [0] [0]等价。
十二、定义指向多维数组的指针,即:数组指针
含义:能够指向数组的指针,里面存放数组的地址。
e.g. int array[10]={0};
int (*p) [ 10 ] = &array; //类型可以看成int * [ 10 ]
声明:int (*p)[ 2 ] ;
说明:声明的是一个指向整型数组(内含两个int类型的值)的指针。p的类型是 int* [ 2 ]。
注:数组表示法vs指针表示法
int zip[ 4 ] [ 3 ] = ~~~ ;
int (*zp) [ 2 ] ;
zip [ m ] [ n ] == *(*(zip + m)+n) ;
zp [ m ] [ n ] == *(*(zp+m)+n) ;
十三、变长数组(允许使用变量表示数组维数)
e.g. int sum( int rows , int cols , int ar [ rows ] [ cols ] ) ;
十四、复合字面量
字面量是除符号常量外的常量。
e.g. (int [ 2 ] ){ 10,20 }; // 其中,int [ 2 ] 是复合字面量的类型名
用法:复合字面量是匿名的,所以可以在创建的同时使用它。
e.g. int (*pt ) [ 4 ] ; // 声明一个指向二维数组的指针,该数组内含2个数组元素
pt = (int [ 2 ] [ 4 ]) { { 1,2,3,4} { 5,6,7,8 } } ; // 2*4 的一个数组
十五、指针数组
含义:是用来存放指针的数组。
e.g. int * array[5]; // 存放整型指针的数组
char * array1[5]; // 存放字符指针的数组
十六、易混淆
数组名是该数组首元素的地址,数组名与指向该数组首元素的指针等价。即:ar [ i ] 等价于 *(ar + i)。
两个等价:array=&array[ 0 ] ;
两个例外:1、sizeof (array)//里面的array是整个数组
2、&array // 里面的数组名是整个数组
注:&array+1是指向下一个数组的地址。
十七、一维数组传参
#include <stdio.h>
void txt(int array[])
{}
void txt(int array[10])
{}
void txt(int* array)
{}
void txt2(int *array[20])
{}
void txt2(int **array)
{}
int main() {
int array[10] = {0};
int *array2[20]= {0};
txt(array);
txt2(array2);
}
十八、二维数组传参
void test(int arr[5][6])
{}
//形参的二维数组,行可以省略,列不能省略
void test(int arr[][6])
{}
void test(int (*arr)[6])
{}
int main() {
int arr[5][6]={0};
test(arr);
}
十九、函数指针
&函数名和函数名都是函数的地址。
int Add(int x,int y)
{
return x +y;
}
void calc(int(*pf)(int, int))
{
int a=3;
int b=5;
int ret = pf(a,b);
printf("%d\n",ret);
}
int main()
{
calc(Add);
return θ;
}
与数组指针类似。
二十、函数指针数组
e.g. int (*p[5])(int , int) = { 0 } ;
应用:制作简易计算器。
#include <stdio.h>
int add(int x,int y)
{
return x+y;
}
int subtract(int x, int y)
{
return x-y;
}
int multiply(int x,int y)
{
return x*y;
}
int divide(int x,int y)
{
return x/y;
}
int main()
{
int input=0;
int x=0;
int y=0;
int result=0;
int (*p[])(int,int) = {0,add,subtract,multiply,divide};
printf("请输入:\n");
scanf("%d",&input);
while(input!=0)
{
if (input==0)
printf("退出\n");
else if (input>0 && input<5)
{
printf("请输入:\n");
scanf("%d %d",&x,&y);
result=p[input](x,y);
printf("%d\n",result);
}
else
{
printf("输入错误!");
}
printf("请输入:\n");
scanf("%d",&input);
}
return 0;
}