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编程的书籍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值