Understanding and using c pointers 第八章讀書筆記

tonybai在他的網站上寫了一個本書的知識點綱要

本章主要涉及以下幾方面
1.指針強轉(Casting pointers)
2.訪問硬件設備(Accessing hardware devices)
3.別名與嚴格別名(Aliasing and strict aliasing)
4.使用restrict關鍵字(Use of the restrict keyword)
5.線程(Threads)
6.面嚮對象的技術(Object-oriented techniques)

 

指針強轉(Casting pointers)
For some situations, such as when we need to address memory location zero,we may need to cast a pointer to an integer and then cast it back to a pointer.This is more common on older systems where a pointer’s size is the same size as an integer.However, this does not always work. The approach is illustrated below, where the output is implementation-dependent
在一些情況下比如我們需要訪問內存地址爲0的數據,我們可能需要將指針轉換爲整形,然後再將其轉換爲指針類型。在一些老的系統上(如果指針類型與整形大小相同)這種方法很常見。然而這並不能保證總是有效。這種方法舉例如下,其輸出根據實現而不同。

int num = 8;
int *pi = #
printf("Before: %p\n",pi);
int tmp = (int) pi;
pi = (int *)tmp;
printf("After: %p\n",pi);

將指針轉換爲整形再轉換回來從來就不是一個好的實踐(practice)。如果需要這麼做,考慮使用union

 

訪問特定地址(Accessing a Special Purpose Address)
在嵌入式系統上,訪問特定地址很常見。例如,在一些底層操作系統內核中PC寄存器的顯存地址是0xB800( For example, in some low-level OS kernels the address of video RAM for a PC is 0xB8000)如下:

#define VIDEO_BASE 0xB8000
int *video = (int*) VIDEO_BASE;
*video = 'A';

訪問內存中地址爲0的方法,作者提供了四種方法
1.將指針設置爲0(並不總是有效)
2.將0賦值給整形,然後將整形強轉爲指針
3.使用union
4.使用memset函數將0賦值給指針

memset((void*)&ptr,0,sizeof(ptr));

 

訪問端口(Accessing a Port)

#define PORT 0xB0000000
unsigned int volatile * const port = (unsigned int *) PORT;		/* volatile關鍵字禁止運行時用寄存器存儲端口值,該關鍵字禁止編譯器做一些優化,java也有這個關鍵字 */

作者提醒:It is not a good idea to access volatile memory with a nonvolatile variable. Using such a variable can result in undefined behavior.

 

使用DMA訪問內存(Accessing Memory using DMA)
Direct Memory Access(DMA)是一個幫助在內存與一些設備之間交換數據的底層操作,在這中間不經過CPU。

 

判斷機器使用的是大端還是小端(Determining the Endianness of a Machine)
作者提供了指針強轉的例子,事實上還可以使用union來判斷

int num = 0x12345678;
char *pc = (char*) num;
int i;
for(i = 0; i < 4; i++)
{
	printf("%p: %02x \n",pc,(unsigned char) *pc++);		/* 一般的都是小端法,sun的一些機器是大端的 */
}


別名,嚴格別名與restrict關鍵字(restrict是c99添加的)
一個指針被稱爲另一個指針的別名如果他們指向同一塊內存地址的話。這並不常見並可能呈現一系列問題。如下:

int num = 5;
int* p1 = &num;
int* p2 = &num;

嚴格別名是另一種形式的別名。嚴格別名不允許一種數據類型的指針是另一種數據類型指針的別名。下面的代碼中int類型的指針是short類型的指針,因此違反了嚴格別名的規則

float number = 3.25f;
unsigned int *ptrValue = (unsigned int *) &number;
unsigned int result = (*ptrValue & 0x80000000) == 0;

Strict aliasing does not apply to pointers differing only by sign or qualifier. The following are all valid strict aliases(以下是合法的嚴格別名):

int num;
const int *ptr1 = &num;
int *ptr2 = &num;
int volatile ptr3 = &num;

然而,特定情況下同一數據的多種表示很有用,要避免別名問題有三種方法

1.使用union(Use a union)
2.禁止嚴格別名(Disable strict aliasing)
3.使用char指針(Use a pointer to char)
GCC對於嚴格別名有以下選項:
-fno-strict-aliasing to turn it off
-fstrict-aliasing to turn it on
-Wstrict-aliasing to warn of strict aliasing-related problems

 

多種方式使用union代表一塊數據(Using a Union to Represent a Value in Multiple Ways)

typedef union _conversion
{
	float fNum;
	unsigned int uiNum;
} Conversion;

/* 沒有違反別名原理 */
int isPositive1(float number)
{
	Conversion conversion = {.fNum = number};
	return (conversion.uiNum & 0x80000000) == 0;
}

typedef union _conversion2
{
	float *fNum;
	unsigned int *uiNum;
} Conversion2;
/* 違反了別名原理 */
int isPositive2(float number)
{
	Conversion2 conversion;
	conversion.fNum = &number;
	return (*conversion.uiNum & 0x80000000) == 0;
}

/* 沒有違反別名原理 */
int isPositive3(float number)
{
	unsigned int *ptrValue = (unsigned int *) &number;
	return (*ptrValue & 0x80000000) == 0;
}


嚴格別名(Strict Aliasing)
The compiler assumes that two or more pointers of different types will never reference the same object.This includes pointers to structures with different names but that are otherwise identical. With strict aliasing,the compiler is able to perform certain optimizations. If the assumption is incorrect, then unexpected results may occur.

 

Even if two structures have the same field but different names, two pointers to these structures should never reference the same object如:

typedef struct _person
{
	char* firstName;
	char* lastName;
	unsigned int age;
} Person;

typedef struct _employee
{
	char* firstName;
	char* lastName;
	unsigned int age;
} Employee;

Person* person;
Employee* employee;


However, the pointers can reference the same object if the structure definitions differ only by their name如下:

typedef struct _person
{
	char* firstName;
	char* lastName;
	unsigned int age;
} Person;

typedef Person Employee;

Person* person;
Employee* employee;


使用restrict關鍵字(Using the restrict Keyword)
Using the restrict keyword when declaring a pointer tells the compiler that the pointer is not aliased。
聲明指針時使用restrict關鍵字告訴編譯器該指針沒有別名,這樣允許編譯器生成更有效的代碼,如果使用了別名那麼代碼的執行結果將是不確定的。

如:兩個數組參數都使用了restrict關鍵字,但他們不能指向同一塊內存。

void add(int size,double * restrict arr1,const double * restrict arr2)
{
	int i;
	for(i = 0; i < size; i++)
	{
		arr1[i] += arr2[i];
	}
}

/* 像下面這三行就沒有問題 */
double vector1[] = {1.1,2.2,3.3,4.4};
double vector2[] = {1.1,2.2,3.3,4.4};
add(4,vector1,vector2);

double vector1[] = {1.1,2.2,3.3,4.4};
double *vector3 = vector1;	/* 這樣就有問題了 */
add(4,vector1,vector3);		/* 這樣就有問題了 */
add(4,vector1,vector1);		/* 這樣就有問題了 */

一些標準庫函數就使用了restrict關鍵字如等:

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
char *strcpy(char * restrict s1, const char * restrict s2);


線程與指針
使用了unix系列使用的POSIX線程,表示沒用過
線程間共享指針——涉及到了互斥量mutex等
使用函數指針支持回調(GUI編程中可能用的比較多)

 

面向對象技術(Object-Oriented Techniques)
創建與使用不透明指針(Creating and Using an Opaque Pointer)
在C中使用不透明指針來實現數據封裝 One approach declares a structure without any implementation details in a header file

 

C語言中的多態(Polymorphism in C)

/**
 *
 * 使用C語言模擬面向對象,gcc編譯通過,會有編譯器警告
 *
 */
#include <stdio.h>
#include <stdlib.h>

typedef void (*fptrSet)(void*,int);
typedef int (*fptrGet)(void*);
typedef void (*fptrDisplay)();

typedef struct _functions
{
	fptrSet setX;
	fptrGet getX;
	fptrSet setY;
	fptrGet getY;
	fptrDisplay display;
} vFunctions;

typedef struct _shape
{
	vFunctions functions;
	int x;
	int y;
} Shape;

void shapeDisplay(Shape *shape)
{
	printf("Shape\n");
}

void shapeSetX(Shape *shape,int x)
{
	shape->x = x;
}

void shapeSetY(Shape *shape,int y)
{
	shape->y = y;
}

int shapeGetX(Shape *shape)
{
	return shape->x;
}

int shapeGetY(Shape *shape)
{
	return shape->y;
}

Shape* getShapeInstance()
{
	Shape* shape = (Shape*) malloc(sizeof(Shape));
	shape->functions.display = shapeDisplay;
	shape->functions.setX = (fptrSet)shapeSetX;
	shape->functions.getX = (fptrGet)shapeGetX;
	shape->functions.setY = (fptrSet)shapeSetY;
	shape->functions.getY = (fptrGet)shapeGetY;
	shape->x = 100;
	shape->y = 100;
	return shape;
}

typedef struct _rectangle
{
	Shape base;
	int width;
	int height;
} Rectangle;

void rectangleSetX(Rectangle* rectangle,int x)
{
	rectangle->base.x = x;
}

void rectangleSetY(Rectangle* rectangle,int y)
{
	rectangle->base.y = y;
}

int rectangleGetX(Rectangle* rectangle)
{
	return rectangle->base.x;
}

int rectangleGetY(Rectangle* rectangle)
{
	return rectangle->base.y;
}

void rectangleDisplay()
{
	printf("Rectangle\n");
}

Rectangle* getRectangleInstance()
{
	Rectangle* rectangle = (Rectangle*) malloc(sizeof(Rectangle));
	rectangle->base.functions.display = rectangleDisplay;
	rectangle->base.functions.setX = (fptrSet)rectangleSetX;
	rectangle->base.functions.getX = (fptrGet)rectangleGetX;
	rectangle->base.functions.setY = (fptrSet)rectangleSetY;
	rectangle->base.functions.getY = (fptrGet)rectangleGetY;
	rectangle->base.x = 200;
	rectangle->base.y = 200;
	rectangle->height = 300;
	rectangle->width = 500;
	return rectangle;
}

main()
{
	Shape* sptr = getShapeInstance();
	sptr->functions.setX(sptr,35);
	sptr->functions.display();
	printf("%d\n",sptr->functions.getX(sptr));
	
	Rectangle* rptr = getRectangleInstance();
	rptr->base.functions.setX(rptr,75);
	rptr->base.functions.display();
	printf("%d\n",rptr->base.functions.getX(rptr));
	
	printf("\n****\n\n");
	
	Shape *shapes[3];
	shapes[0] = getShapeInstance();
	shapes[0]->functions.setX(shapes[0],35);
	shapes[1] = getRectangleInstance();
	shapes[1]->functions.setX(shapes[1],45);
	shapes[2] = getShapeInstance();
	shapes[2]->functions.setX(shapes[2],55);
	
	int i;
	for(i = 0; i < 3; i++)
	{
		shapes[i]->functions.display();
		printf("%d\n",shapes[i]->functions.getX(shapes[i]));
	}
}

這裏有一本使用ANSI C進行OOP編程的書籍

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章