第七章 指针的概念

如何访问、如何引用、如何存储????

问题:
1、如何访问变量?
通过变量名称来访问变量
2、如何访问指针变量?
通过指针变量名

问题一:如何通过指针访问值?

#include <stdio.h>

int main()
{
    int number = 15;
    int *pointer = &number;
    int result = 0;

    printf("%d\n",*pointer);

    result = *pointer + 5;

    printf("%d\n",result);
    return 0;
}

7.1 指针初探

指针是C语言中最强的工具

int number = 5;

这条语句会分配一块内存来存储一个整数,使用变量number的名称可以访问这个整数,
值5存储在这个区域中。
计算机用一个地址引用这个区域。

存储地址的变量称为指针(pointers),存储在指针中的地址通常是另一个变量,

指针的工作原理

int number = 99;
int *pnumber = &number;

指针pnumber含有另一个变量number的地址,变量number是一个值为99的整数变量。
指针只是一个存储内存位置的地址。
存储在pnumber中的地址是number第一个字节的地址。
编译器需要知道指针 指向的变量类型。
char 类型值的指针指向占有一个字节的值
而long类型值的指针指向占有4个字节的值

  • 给定类型的指针写成type*,其中type 是任意给定的类型。

类型名void表示没有指定类型,所以void类型的指针可以包含任意类型的数据项地址。类型void常常用作参数类型,或以独立于类型的方式处理数据的函数的返回值类型。任意类型的指针都可以传送为void*类型的值,在使用它是,再将其转换为合适的类型。

7.1.1 声明指针

(1)声明指针变量时,指针变量前一定要有“ * ”符号
(2)定义指针时,要加上类型标示符。

类型  *指针变量名

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

       int     *pnumber; &&&& int*    pnumber

以上两条语句完全相同,但使用时最好始终使用其中一种,没有初始化的指针是非常危险的,所以应总是在声明指针时对他进行初始化。

初始化pnumber,使他不指向任何对象。

                      int  *pnumber = NULL;

寻址运算符&:获取变量的地址。
取消引用运算符*:获取指针指向的地址中的内容,并对该地址中的内容进行赋值操作。

p = &a;

NULL是在标准库中定义的一个常量,对于指针它表示0。NULL是一个不指向任何内存位置的值。
NULL在头文件< stddef.h> 、<stdlib.h>、<stdio.h>、<string.h>、< time.h>、<wchar.h>和<locale.h>中定义
只要编译器不能识别NULL,就应在源文件中包含<stddef.h>头文件

如果用已声明的变量地址初始化pointer变量,可以使用寻址运算符。如下所例:

int number = 99;
int *pnumber = &number;

pnumber的初值是number变量的地址。注意,number的声明必须在pnumber的生命之前。

用相同的语句声明一般的变量和指针

double value,  *pVal, fnum;

这条语句声明了两个变量,以及一个指向double的变量pVal

int  *p  ,  q;

声明一个指针和一个变量

7.1.2 通过指针访问值

使用间接运算符*可以访问指针所指的变量值
取消引用运算符(dereferencing operator )取消对指针的引用。

int  number = 15;
int *pointer =  &number;
int result  = 0;

7.1.3 使用指针

星号*表示访问指针变量所指向的内容

在算术语句中使用取消引用的指针

*pnumber += 25;

将变量pnumber所指向的地址中的值增加25

int value = 999;
pnumber = &value;

指针可以包含同一类型的任意变量的地址,所以使用一个指针变量可以改变其他很多变量的值,只要它们的类型和指针相同。

运算符++和一元运算符* (&)的优先级相同,且都是从右往左计算的。

pvalue 和 value 是相同的所以用任何一个都可以

7.14 指向常量的指针

常量指针与指针常量的指针的区别是

  • 常量指针是指在指针中存储的地址不发生改变。
    指向常量的指针是指针指向的值不发生改变。

使用const关键字声明指针时,该指针指向的值不能改变。

long value = 9999L;
const long *pvalue = &value;

指针本身不是常量,所以可以改变指针指向的值

long number = 8888L;
pvalue = &number;
把变量number的地址赋值给指针变量pvalue,指针变量是一个含有地址的变量,所以可以使用指针变量名作为参数即
(&value 等价于pvalue)
指针是另外一个变量的地址
  • 指针可以改变指针中储存的地址,但不能改变指针的指向的值

7.1.5 常量指针

什么是常量指针(常量指针的概念是什么)?

  • 常量指针是指在指针中存储的地址不发生改变。

下面的语句可以是指针总是指向相同的对象:

int count  = 43;
int *const pcount = &count;

可以创建一个常量指针,它指向一个常量值:

int item = 25;
const  int  *const pitem = &item;

7.1.6 指针的命名

最好将P作为指针名的第一个字母

7.2 数组和指针

  • 数组是相同类型的(对象)元素集合,在内存中占据着一块连续的存储空间,每一个元素都有一个确定的地址值。因此可以利用指针对数组中的每一个元素进行操作。

  • 指针是一个变量,它的值是给定类型的另一个变量或常量的地址。
    数组名可表示数组的首地址,即数组第一个元素所在的位置
    数组名等于数组第一个字节的地址,&multiple[0] 等于数组第一个元素的第一个字节

有三种方式可以实现对数组元素的访问
(1)通过下标访问 x[i](2)通过地址访问 (3)通过指针访问

用scanf_s输入一个字符,可以用一下语句

char single = 0;
scanf_s("%c", &single, sizeof(single));

如果读入字符串,可以编写如下代码

char multiple[10];
scanf_s("%s", multiple, sizeof(multiple));

数组和指针有一个重要的区别:可以改变指针包含的地址,但不能改变数组名称引用的地址。

编译器知道,给地址值加1时,就表示要访问该类型的的下一个变量,这就是为什么声明一个指针时,必须要指定的该指针指向的变量类型。
数组名称是一个固定的地址,而不是一个指针,可以在表达式中使用数组名及其引用的地址,但不能修改它。

7.3 多维数组

board 是char型二维数组的地址,board[0]是char型1⃣️维子数组的地址,它是board的一个子数组,&board[0][0]是char型数组元素的地址。
如果使用board获取第一个元素的值,就需要使用两个间接运算符,
如果只使用一个*,只会得到子数组的第一个元素。
board引用子数组中第一个元素的地址
board[0]board[0]board[0]引用对应子数组中第一个元素的地址。
用两个索引值访问存储在数组元素中的值。

7.3.1 多维数组和指针

7.3.2 访问数组元素

问题:如何实现board)

访问数组元素的指针表达式

board 0 1 2
0 board[0][0] board[0][1] board[0][2]
0 * board[0] *(board[0] + 1) * ( board[0] +2)
0 **board (board+1) (board + 2
1 board[1][0] board[1][1] board[1][2]
1 * board[1 ] *(board[1 ] + 1) * ( board[1] +2)
1 (board+3) (board +4) (board+5)
2 board[2][0] board[2][1] board[2][2]
2 *board[2] (board[2]+1) (board[2]+2)
board 0 1 2
0 board[0][0] board[0][1] board[0][2]
1 board[1][0] board[1][1] board[1][2]
2 board[2][0] board[2][1] board[2][2]
board 0 1 2
0 * board[0] *(board[0] + 1) * ( board[0] +2)
1 * board[1] *(board[1] + 1) * ( board[1] +2)
2 *board[2] (board[2]+1) (board[2]+2)
board 0 1 2
0 **board * ( * board+1) (board + 2
1 (board+3) (board +4) (board+5)

7.4 内存的使用

指针是一个强大的编程工具
c语言还有一个功能:动态内存分配
在程序的执行期间分配内存时,内存区域中的这个空间称为堆(heap),还有另一个内存区域,称为堆栈(stack),其中的空间分配给函数的参数和本地变量。在执行完该函数后,存储参数和本地变量的内存空间就会释放。
堆中的内存是由程序猿控制的

7.4.1 动态内存分配:malloc()函数

动态内存分配(dynamic memory allocation)
在运行时分配内存的最简单的标准库函数是malloc()函数,
使用malloc函数需要指定要分配分内存字节数作为参数。malloc函数返回所分配内存的第一个字节的地址。

int *pNumber = (int*)malloc(100);
int *pNumber = (int*)malloc(25*sizeof(int));

类型转换(int*)将函数返回的地址转换成int类型的指针

int *pNumber = (int*)malloc(25*sizeof(int));
if(pNumber)
{
    //code to deal with memory allocation failure
}

7.4.2释放动态分配的内存

动态分配了一些内存时,没有保留对它们的引用,就会出现内存泄露,此时无法释放内存。

必须能访问引用内存块的地址

free(pNumber)
pNumber = NULL;

free()函数的形参是void*类型,所有的指针类型都可以自动的转换为这个类型,所以可以把任意类型的指针作为参数传送给free()函数
在指针指向的内存释放后,应总是把指针设置为NULL;
在释放内存后,应总是把指向堆内存的指针设置为NULL,这样就不会使用不再可用的内存了使用不再可用的内存总是很危险的。
malloc函数的参数是size_t类型
如果size_t对应4字节的无符号的整数,则一次至多可以分配4294967295个字节

7.4.3 用calloc函数分配内存

它把内存分配为给定大小的数组,
它初始化了所分配的内存,所有的位都是0
数组的元素个数和数组元素占用的字节数,这两个参数的类型都是size_t,
calloc函数不知道数组的类型,所以分配的内存区域地址返回为void*类型。

int *pNumber =(int*)calloc(75,sizeof(int));

如果不能分配所请求的内存,返回值就是NULL;

int *pNumber = calloc(75,sizeof(int));
pPrimes = calloc((size_t)total,sizeof(unsigned long long));
if(pPrimes == NULL)
{
    printf(" Not enough memory.It's the end I am afraid.\n");
    return 1;
}

7.4.4 扩展动态分配的内存

realloc函数需要两个参数:包含地址的指针。要分配的新内存的字节数
如果realloc的第一个参数是NULL,就分配第二个参数制定的新内存。如果第一个参数不是NULL,但不指向以前分配的内存,或者指向已释放的内存,结果就不确定了。

7.5使用指针处理字符串

存储字符和引用字符串

  • 1、char类型的数组元素存储字符串
  • 2、char类型的指针变量引用字符串
  • 3、声明指针变量时只是创建了字符串变量并没有指定一个存储字符串的地方
    要存储字符串,需要分配一些内存,指针变量中存储其地址。
    因此使用指针变量存储字符串地址是用动态内存分配功能非常有效。

指针首先是一个变量,指针只是存储了另一个内存位置的地址的变量。

  • 声明一个char类型的指针变量 即只创建了指针,没有指定一个存储字符串的地方,
char *pString = NULL;
  • 指针只是一个存储另一个内存位置的地址的变量。
const size_t BUF_SIZE = 100;
char buffer[BUF_SIZE]
scanf_s("%s", buffer, BUF_SIZE);

size_t length = strnlen_s(buffer, BUF_SIZE) + 1;
char *pString = malloc(length);
if(!pString)
{
    printf("memory allocation failed.\n");
    return 1;
}
  strcpy_s(pString,length,buffer);  
  printf(" %s ", pString);
  free(pString);
  pString = NULL;

这段代码把一个字符串读入一个char数组中,给读入的字符串分配堆上的内存,在将字符串复制到pString引用的内存中。就允许重用buffer来读取更多的数据。

7.5.1使用指针数组

  • 创建一个指针数组,存储字符串的位置。
char *pS[10] = { NULL };

这个语句声明一个数组pS,数组包含10个char*类型的元素。

  • 数组中的每一个元素都可以存储字符串的地址。
  • 初始化列表中只有一个NULL,NULL将任意大小的指针数组中的所有元素都初始化为NULL。
#define STR_COUNT 10
const size_t BUF_SIZE = 100;
char buffer[BUF_SIZE];
char *pS[STR_COUNT] = { NULL };
size_t str_size = 0;

for(size_t  i = 0; i < STR_COUNT; ++i)
{
    scanf_s("%s",buffer, BUF_SIZE);
    str_size = strnlen_s(buffer, BUF_SIZE) + 1;
    pS[i] = malloc(str_size);
    if(!pS[i] return 1;
    strcpy_s(pS[i],str_size,buffer);
}
    
for(size_t i =0; i < STR_COUNT ; ++i)
{
    free(pS[i]);
    pS[i] = NULL;
}
  • 数组记号
    使用指针名和索引值共同构成
    数组记号来存储相同类型的几个数据类型
  • 指针数组是指针记号
    指针名和索引值构成
    数组记号和指针记号的区别是什么呢?
    指向一块推内存的指针变量不仅可以用指针记号还可以用数组记号
int count = 100;
double *data = calloc(count,sizeof(count));

for(int i = 0; i<count; ++i)
{
     data[i] = double(i+1)*(i+1);
}
  • pS数组的每个元素都保存从键盘读取的一个字符串的地址

问题:

1、字符串的存储与引用方式有几种类型。
2、 如何处理任意长度的字符串
3、如果不知道要输入多少个字符串,该怎么办?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,420评论 3 44
  • 前言 最近真的是忙的不可开交,公司一直给安排任务,连学习和写笔记的时间都没有了,落下好几次课的笔记都没有写,所以我...
    Xiho丶阅读 1,507评论 1 12
  • C语言指针的总结 1. 变量 不同类型的变量在内存中占据不同的字节空间。 内存中存储数据的最小基本单位是字节,每一...
    xx_cc阅读 3,674评论 11 39
  • 广意_阅读 292评论 1 4
  • (五) 胖子他们在我那玩了一宿,我跟他们说了楼上老太太的事,他们说老人家本来就是这样的,特别是孤寡老人,孤独惯了,...
    最后一只独角兽阅读 798评论 0 2