Uderstanding and using Pointers 读书笔记

如何阅读指针?

从右向左读。

比如 const int *pci;

阅读指针

虚拟内存和虚拟内存地址是什么?

一个应用程序,在虚拟内存地址里也许是连续的,但是在物理内存里也许是分隔开来的。

虚拟内存和物理内存的映射一般是操作系统主导完成。

程序一般返回的内存地址都是虚拟内存地址。

如何手动 实现 null ?

在许多库里面,NULL 被定义为下面这样的宏

#define NULL ((void *)0)

值为 NULL 的指针不能做什么事情?

不能进行解码,一旦进行解码,就会出现程序终止,因为这个指针实际上并没有含有一个有效的地址。

c 程序编译后,有多少种类型的内存?

Static/Global

当程序运行的时候,这两种类型的变量就一直存在,直至程序终止。

这两个变量的区别是,Global 类型的变量可以被全局访问,而 Static 的变量受函数的范围约束。

Automatic

这种类型的变量在函数里面声明,并且只有当函数被调用的时候才会创建。

他的范围受限于函数,他的生命周期也只有在函数运行的那段时间。

Dynamic

这回总类型的变量,他的内存是从堆里面分配来的,这个变量会一直存在,直到他被声明释放,才会消失。

他的范围受限于指针,或者说,是指针指向的那个内存地址。

总结下

生命周期

大部分系统下,数据类型的大小是多少?

不同系统下,同一种数据类型的大小可能不同。

数据类型的大小

%p 的意思是什么?

他的意思是将指针给解析出来,所以你直接传递变量给他去不行的,你要传递地址,也就是指针。

const int 和 int const 的区别

变量类型和 const 关键字的位置顺序并没有什么影响。

下面两个声明都是等同的。

但是 const 在 * 的前后会有影响。

const int *pci;

int const *pci;

const int *p 是怎么回事?

const int *p 是能够使用那些指向 const int 或者 int 类型的指针来赋值的,但是不能改变指针指向的数值,也就是指针解码后的数据是不能够改变的。

int *const p 是啥意思?

这个的意思是, p 这个指针,指向了 int 类型的变量,但是 p 本身已经被声明为了 const 类型,他是不能够改变的。

也就是 p 不能再被赋予其他地址了,但是 p 解码后的数据是能够改变的。

const int * const p 是怎么回事?

这意味着你的指针是 const 的,你的指针指向的类型也是 const 的,所以你无法修改指针的值,也无法修改解码指针后的数据。

const int * const * p 是什么意思?

简单来说,就是一个指针,指向了 「const int * const 类型的指针」。

数组长度是在什么时候确定的?

在程序运行的时候,而不是在编译的时候。

然后,一旦数组被编译创建了,就不能够再改变他的大小了。

动态内存分配函数有几种?

动态内存分配

malloc 的形参有需要注意的?

函数原型

void* malloc(size_t);

关于他的形参,有几点需要注意的。

如果你给这个形参传入了负数,那么有的系统会直接报错,有的系统则会返回一个 NULL 值。

如果你给形参赋值 0 ,那么他也许会返回 NULL 值,或者他会返回一个指针指向一个 0 bytes 的内存区域。

如果,你给形参赋值 NULL,则系统会警告,然后返回一个指向 0 bytes 的内存区域的指针。

使用 malloc 的最佳实践是什么?

由于 malloc 也许会返回 NULL ,所以需要在使用 malloc 返回的指针之前,检查一下他返回的值是不是 NULL

int *pi = (int*) malloc(sizeof(int));

if(pi != NULL)
{
	// Pointer should be good
}else {
	// Bad pointer
}

怎么使用 malloc ?

在编译器的角度来看,= 符号有两种状态,一种是初始化,一种则是赋值,这是不同的两种状态。

错误

static int *pi = malloc(sizeof(int));

正确

static int *pi;
pi = malloc(sizeof(int));

清理内存是怎么做到的?

只要将内存全部设置成二进制的 0 就行了。

calloc 函数的原型是怎样?

calloc 函数会同时分配和清理内存,他的函数原型是这样的。

void *calloc(size_t numElements, size_t elementSize);

什么时候用 calloc ?

当你的内存需要清空的时候,使用 calloc

当某个指向一块内存区域的指针被释放后,这个指针指向什么?

他被释放后,实际上还是指向原本指向的地方,只不过不再能被调用。

dangling pointer 是什么?有什么危害?

你释放了一个指针,然而这个指针没有被赋值为 null ,那么这个指针就是 dangling pointer ,因为他还是指向着之前指向的那块内存区域。

dangling pointer 的危害是:

1、你不能预测这个内存区域是否还会被进行访问。

2、如果这个内存区域不能被访问,而你访问了,则会发声 segment fault

3、隐藏的安全风险

automatic variables 的其他名字是什么?

他也叫做 local variables

被分配到 stack 上。

真正释放指针的最佳实践是什么?

在释放指针后,将指针赋值为 NULL

使用 void 类型有什么需要注意的?

使用 void 类型的形参,可以接受任何类型的变量。

但是如果你没有显示将这个变量强制转换成了 void 类型,那么这个编译器是会提示你警告的。

你只要将这个变量强制转换成了 void 类型就行了。

函数指针和返回指针的函数,别搞混了

函数指针,是指向函数的指针。

而返回指针的函数,只是返回指针。

如何将函数指针简化?

typedef int (*funcptr)(int);

...

funcptr fptr2;

fptr2 = square;

printf("%d squared is %d\n",n, fptr2(n));

上面的 typedef 语句,让 funcptr 这个函数变成了一种类型。

返回一个指针,这个指针指向 int 类型

不要随便对函数指针进行强制类型转换

这种活动一般来说都不能保证每次都能成功。

指针和数组名的区别是什么?

指针可以赋值其他地址,而数组名不行。

如何为函数设定没有固定长度的数组?

使用数组标记法

就是通过一个函数的形参 arr[] 来达到目的的。

void displayArray(int arr[], int size) 
{ 
    for (int i = 0; i < size; i++)
    { 
        printf("%d\n", arr[i]); 
    } 
} 

int vector[5] = {1, 2, 3, 4, 5}; 

displayArray(vector, 5);

指针标记法

void displayArray(int* arr, int size) 
{ 
    for (int i = 0; i < size; i++) 
    { 
        printf("%d\n", arr[i]); 
    } 
}

如何为函数传递二维数组?

你可以使用下面的两种方法来传递二维数组

void display2DArray(int arr[][5], int rows){}

或者是

void display2DArray(int (*arr)[5], int rows){}

需要注意的是,如果写成这样子就没有用了,他表示 arr 数组含有五个元素,每个元素都是指向 int 类型的指针。

void display2DArray(int *arr[5], int rows){}

同理,如果是这样子写就是传递一个三维数组了

void display3DArray(int (*arr)[2][4], int rows)

分配动态数组的方法是什么?

int rows = 2;
int columns = 5;

int **matrix = (int **) malloc(rows * sizeof(int *));

for (int i = 0; i < rows; i++) {
        matrix[i] = (int *) malloc(columns * sizeof(int));
}

如何构造参差不齐的数组?

int (*(arr2[])) = {
    (int[]) {0, 1, 2, 3},
    (int[]) {4, 5},
    (int[]) {6, 7, 8}};

构造长的一样的数组

int (*(arr1[])) = {
    (int[]) {0, 1, 2},
    (int[]) {3, 4, 5},
    (int[]) {6, 7, 8}};

为什么不提倡使用参差不齐的数组?

因为你没法记住某个元素的长度究竟是多少,一不小心你就数组越界了。

string 的定义是什么?

string 是一段符号并且以 ASCII 码 NUL 符号结尾的东西。

NUL 意味着 \0

string 一般存储在数组里,在内存里一般分配到堆。

然而,并不是所有的字符数组都是 string,因为他不一定是以 NUL 结尾的。

string 的长度是怎么算的?

一般计算 String 长度的时候,都会忘记 NUL 这个字符,所以如果是分配给某个 string 的长度,一定要记得分配上 NUL 字符的长度。

NUL 和 NULL 有什么区别?

NULL 和 NUL 是不一样的。

NULL 是个特殊的指针,被定义为 ((void*)0)

而 NUL 则是一个 char 类型的字符,并且被定义为 \0

他们两个是不可能互相转换的。

字符变量的长度是多少?

printf("%d\n",sizeof(char));

printf("%d\n",sizeof('A'));

当执行的时候,第一个输出是 1,第二个输出是 4,第二种类型是 character literal,他是 int 类型,这是语言的设计问题。

string 是否可变?

在大部分编译器看来,string 都是常量,但是对于 GCC 编译器来说,却是有办法改变 string 的值。

char *tabHeader = "Sound"; 

*tabHeader = 'L'; 

printf("%s\n",tabHeader); // Displays "Lound"

所以最安全的是这样子声明

const char *tabHeader = "Sound";

错误的初始化数组方法

char header2[];

header2 = "Media Player";

左边是数组名是指针,右边是 string,怎么可以这样子赋值

怎么让字符指针初始化为字符串?

char *header = (char*) malloc(strlen("Media Player")+1); 

strcpy(header,"Media Player");

使用 malloc 来确定字符串长度的时候,需要注意什么?

一定要记得算上 NUL 这个字符。

不要使用 sizeof 来测量 string 的长度是多少,因为 sizeof 会返回 array 或者 pointer 的大小,而不是 string 的长度。

你应该使用 len 函数来测量。

指针指向结构体的话,解析结构体内元素的方法是什么?

Person *ptrPerson; // Person 是结构体

ptrPerson = (Person*)malloc(sizeof(Person));

ptrPerson->firstName = (char*)malloc(strlen("Emily")+1);

strcpy(ptrPerson->firstName,"Emily");

ptrPerson->age = 23;

另一种解析方法

Person *ptrPerson;

ptrPerson = (Person*)malloc(sizeof(Person));

(*ptrPerson).firstName = (char*)malloc(strlen("Emily")+1);

strcpy((*ptrPerson).firstName,"Emily");

(*ptrPerson).age = 23;

不好的指针申明方式有哪些?

int* ptr1, ptr2;

这个的声明方式, ptr1 才是指针,而 ptr2 是 int 类型的变量。

所以正确的声明方式是

int *ptr1, *ptr2;

错误的内存管理方法

不应该两次 free 一个指针,因为那样子会出错。

DMA 是什么?

Direct Memory Access (DMA)

这是一种 low-level 操作,他用于在内存和其他设备之间传输数据。

这个功能并不是 ANSI C 的功能之一,他是操作系统提供的。

指针别名是怎么回事?

当两个指针指向同一个地址的时候,第二个指针就叫做第一个指针的别名。

如何防止指针别名?

使用 restrict 关键字,在声明这个指针的时候,就使用 restrict 关键字。

他表示这个指针指向的内存是独一无二的,只有他这个指针才能指向,其他指针不能指向这个内存区域。

经常使用 restrict 关键字,有利于提高程序的性能。

posted @ 2015-09-02 13:04  0x1D  阅读(260)  评论(0编辑  收藏  举报