C语言-第七章-指针

1 指针

声明一个指向int类型变量的指针:

int *pointer = NULL;  

声明指针时一定要初始化!

int number = 10;  
int *pointer = &number;  

可以和其它变量一起声明:

int *p, q;  //声明了指针变量p和整型变量q  

使用间接运算符*可以访问指针所指的变量值。这个运算符也称为取消引用运算符:

int number = 3;
int *pointer = &number; //pointer变量含有number变量的地址
int result = 0;
result = *pointer + 5;  //8  

使用指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
int num1=0L, num2=0L, *p=NULL; //初始化指针变量p
p = &num1; //p指向num1
num2 = ++*p; //p和num2都是1
p = &num2; //p指向num2
num2 += *p; //num2等于2,所以p指向的值也是2
printf("num1=%d, num2=%d, pvalue=%d.", num1, num2, *p); //1,2,2
*p = 4; //直接赋值
printf("num2=%d pvalue=%d.", num2, *p); //*p等于num2等于4
return 0;
}

以上可以看出p可以指向多个变量,并改变它们的值,只要它们的变量类型相同。

*p++    //一元运算符从右向左计算,所以是先递增地址,再取消引用得到值,与scanf()配合使用:
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main()
{
int value=0, *p=NULL;
printf("输入一个数:");
p = &value;
scanf("%d", p); //这里p和&value等价
printf("你输入的数为:%d", value);
return 0;
}

指向常量的指针:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
int value = 999;
const long *p = &value; //声明为不动值
*p = 888; //*p是不允许改变的,但可以改变p指向的地址。如long num=888;p=&num;
return 0;
}

常量指针:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
int value = 999;
int *const p = &value;
p = &value; //p是常量,不允许改变。但可以*p=888来改变常量的值。
printf("%d", *p);
return 0;
}

结合以上两个:

const int *const p = NULL;  //这时候p完全不能改变

2 数组和指针

char multi[] = "my string";
char *p = &multi[0];
printf("%p", p);
p = multi;
printf("%p", 0);//和第二行一样的结果

3 多维数组

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main()
{
char board[3][3] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'}
};
printf("value of board[0][0]:%8c\nvalue of *board[0]:%8c\nvalude of **board:%8c",board[0][0], *board[0], **board); //都是1
return 0;
}

上面有

**(board+1)=4;  
**board+16=A;  
*(*board+1)=2;  //*board==&board[0][0]  

4 内存的使用

4.1 动态内存分配:malloc()

int *p = (int *)malloc(100);

100是malloc()的参数,表明要申请的内存大小,返回第一个字节的地址,所以用int *来修饰malloc()表明要把这100B保存为int型,那么能存25个int值。malloc()在<stdlib.h>中。

4.2 找质数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int main()
{
unsigned long *p=NULL, trial=0; //质数都是正的,用无符号类型
bool found=false; //加stdbool.h
size_t total=0,count=0;

printf("输入你想找的质数个数,大于4个:");
scanf("%u", &total); //输入10000,最后一个质数是104729,花了6.817秒
total = total<4U ? 4U : total; //已知了2、3、5,所以最好也要4个以上

p = (unsigned long *)malloc(total * sizeof(unsigned long)); //申请空间,加头文件stdlib.h。sizeof()返回的是size_t类型,上面total也声明为此类型
if(p == NULL)
{
printf("内存不足!");
return 1;
}

*p = 2UL; //第一个质数
*(p+1) = 3UL;
*(p+2) = 5UL;
count = 3U; //现在有三个质数
trial = 5U; //现在的质数为5

while(count<total)
{
trial += 2UL; //从下一个测试数开始,质数肯定是奇数!
size_t i=0;
for(; i<count; i++)
if(!(found = (trial % *(p+i)))) //除数是已经的质数则会快很多,如果能除尽就跳出循环
break;

if(found) //发现质数!!!
*(p+count++) = trial; //将发现的质数加入队列
}

size_t j=0;
for(; j<total; j++)
{
if(!(j%5U)) //每行打印五个
printf("\n");
printf("%12lu", *(p+j));
}
free(p);
return 0;
}

4.3 calloc()

int *p = (int *)calloc(100, sizeof(int));  

与malloc()类似,只是申请成功后会用0初始化所分配的空间,两个参数都是size_t类型。

4.4 释放动态分配的内存

当动态分配了一些内存时,没有保留对它们的引用,就会出现内存泄漏,此时无法释放内存。这通常发生在循环内部,由于没有释放不再需要的内存,程序会使用更多的内存,最终占用所有内存。

使用free(p);释放内存。

4.5 重新分配内存

relloc()可以征用前面通过malloc() or calloc() or relloc()分配的内存。

relloc(p, count*sizeof(int));   //第一个参数是以前申请的内存首地址,第二个参数是需要的内存大小,很明显第二个参数要小于以前申请的内存大小。  

5 使用指针处理字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>

const size_t BUFFER_LEN = 512; //定义缓冲区大小

int main(void)
{
char buffer[BUFFER_LEN]; //存储字符串
char *pS[3] = { NULL }; //输入的三行字符串保存在这
char *pbuffer = buffer; //指向缓冲区
size_t index = 0; //缓冲区中可用的位置

printf("\nEnter 3 messages that total less than %u characters.", BUFFER_LEN-2);

int i=0 ;
for(; i<3 ; i++)
{
printf("\nEnter %s message\n", i>0 ? "another" : "a" );
pS[i] = &buffer[index]; //保存读入的字符串

for( ; index<BUFFER_LEN ; index++) //如果缓冲区还有空闲
if((*(pbuffer+index) = getchar()) == '\n') //检测是否到行尾
{
*(pbuffer+index++) = '\0';
break;
}

if((index == BUFFER_LEN) && ((*(pbuffer+index-1) != '\0') || (i<2)))
{
printf("\nYou ran out of space in the buffer.");
return 1;
}
}

printf("\nThe strings you entered are:\n\n");
int j = 0 ;
for(; j<3 ; j++)
printf("%s\n", pS[j]);

printf("The buffer has %d characters unused.\n", BUFFER_LEN-index);
return 0;
}

地址也可以减

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(void)
{
long long a=1LL,b=2223LL;
printf("%p %p\n",&a, &b);
printf("%d",(int)&a-(int)&b); //8
return 0;
}

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(void)
{
short a=1LL,b=2223LL;
printf("%p %p\n",&a, &b);
printf("%d",&a-&b); //1
return 0;
}

gets()存储换行符,并加上终止符’\0’;fgets()不存储换行符。