C语言学习笔记与总结(一)

来源:互联网 发布:mysql创建数据库 编辑:程序博客网 时间:2024/06/13 07:03

一、数据定义

用变量a给出下面的定义
a) 一个整型数(An integer) 
b)一个指向整型数的指针( A pointer to an integer) 
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege
d)一个有10个整型数的数组( An array of 10 integers

e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers) 
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers) 
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) 
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer 
答案是: 
a) int a; // An integer 
b) int *a; // A pointer to an integer 
c) int **a; // A pointer to a pointer to an integer 
d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointers to integers 
等价于int *(a[10]);

f) int (*a)[10]; // A pointer to an array of 10 integers 
g) int (*max_function)(int a); // A pointer to a function a that takes an integer argument and returns an integer

h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer


二、一些特殊关键字的作用


static

关键字static有三个明显的作用:
a)一旦声明为静态变量,在编译时刻开始永远存在,不受作用域范围约束,但是如果是局部静态变量,则此静态变量只能在局部作用域内使用,超出范围不能使用,但是它确实还占用内存,还存在.
b) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
c) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。


const

首先它的作用主要有三点:

1)只读。

2通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3)使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。


其次它的使用方法,我们先通过一个例子来了解一下:

下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;

/******/
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。


最后总结一下对全局数据使用const的两种策略(const防止了程序的任意部分的可以错误修改程序的发生):

a)遵循外部变量的惯用规则,在一个文件中定义声明,在其他文件中尽兴引用声明(使用关键里extern)

b)将常量放在一个include文件中。这是还必须使用静态外部存储类(加关键字static)

小细节:在与ANSI兼容的编译器中,下面的代码将产生一个错误信息:

const int nochange;

nochange=12;

然而,可以初始化一个const变量。因此,下面代码是对的:

const int nochange = 12;

上面的声明使nochange成为一个只读变量。在初始化后就不能改变它。


volatile

限定词volatile告诉编译器该变量除了可被程序改变外还可被其他代理改变。语法同const。

精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

几个典型的例子:

?; 并行设备的硬件寄存器(如:状态寄存器)
?; 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
?; 多线程应用中被几个任务共享的变量

一个面试题:

?; 一个参数既可以是const还可以是volatile吗?解释为什么。

?; 一个指针可以是volatile 吗?解释为什么。

?; 下面的函数有什么错误:int square(volatile int *ptr){return *ptr * *ptr;}

下面是答案:
?; 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
?; 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。
?; 这段代码有点变态。这段代码的目的是用来返回指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr) 
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由于*ptr的值可能被意想不到地该变,因此ab可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr) 
{
int a;
a = *ptr;
return a * a;
}


restrict

它只可用于指针,并标明指针式访问一个数据对象的唯一且初始的方式。通过允许编译器优化某几种代码增强了计算机的支持。
int ar[10];
int * restrict restar =(int *)malloc(10 * sizeof(int));//指针restar是访问malloc()分配内存的唯一且初始的方式
int * par =ar;

for(int n = 0;n<10;n++)
{
    par[n] +=5;
    restar[n] += 5;
    ar[n] *=2;
    par[n] +=3;
    restar[n] +=3;
}
知道了restar是访问它所指向的数据块的唯一初始方式,编译器就可以用具有同样效果的一条语句来代替包含restar的两个语句:
restar[n] +=8 //可以进行替换
然而par泽不会精简。
restrict使编译器可以放心的寻找计算的捷径。
更加典型的利用时C库中的两个函数。在C99标准下,他们的原型如下:
 void * memcpy(void * restrict s1,const void *restrict s2,size_t n)
 void * memmove(void * s1,const void * s2,size_t n)
其中,memcpy()要求两个位置不重叠,memmove()没有这个要求。restrict使得s1和s2不能访问同一数据块。






0 0
原创粉丝点击