指针和数组

指针和地址

一元运算符&可用于取一个对象的地址,因此,下列语句:

p = &c;

将把c的地址赋值给变量p,我们称p为“指向”c的指针。地址运算符&只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式,常量或register类型的变量。
一元运算符*是间接寻址或间接引用运算符。当它作用于指针时,将访问指针所指向的对象。下面是一个简单的例子。

int x = 1,y = 2, z[10];
int *ip; /* ip是指向int类型的指针 */
ip = &x; /* ip指向x */
y = *ip; /* y的值为ip指向的值,即x的值 */
*ip = 0; /* x的值为0 */
ip = &z[0]; /* ip指向z[0] */

指针都必须指向某种特定类型的数据。void类型的指针可以存放指向任何类型的指针,但它不能间接引用其自身。

指针与函数参数

由于C语言是以传值得方式将参数值传递给被调用函数,因此,被调用函数不能直接修改主调函数中变量了值。如果,需要修改变量中的值,可以变量的指针传递给调用函数。如:

void swap(int *px,int *py){
    int temp;
    temp = *px;
    *px = *py;
    *py = temp;
}

指针与数组
在C语言中,指针和数组之间的关系十分密切,通过数组下标所能完成的任何操作都可以通过指针来实现。下面是一个例子:

int array[] = {1,2,3,4,5,6,7,8,9,10};
int *pa;
pa = array; /* 等价于pa = &array[0] */
printf("%d\n",*(pa+4)); /* 等价于p[4] */

当把数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址。在函数定义中,形式参数

int arr[];

int *arr;

是等价的。通常更习惯于使用后面一种形式,因为它比前者更加直观地表明了该参数是一个指针。

地址算术运算

指针和指针的比较:如果指针p和q指向同一数组的成员,那么它们之间就可以进行类似于==,!=,<,>=的关系比较运算。
指针和整数的加减:

p + n

表示指针p当前指向对象之后第n个对象的地址。在计算p+n时,n将根据P指向的对象的长度按比例缩放,而p指向的对象的长度取决于p的声明。
指针和NULL:指针可以赋值为NULL(0),或和NULL进行比较运算。

字符指针和函数

字符串常量是一个字符数组,例如:

"I am a string"

在字符串得内部表示中,字符数组以空字符'\0'结尾,所以,程序可以通过检查空字符找到字符数组的结尾。字符串常量占据的存储单元数也因此比双引号内的字符数大1。
下面两个定义之间的定义有很大的差别:

char amessage[] = "hello world";
char *pmessage = "hello world";

上述声明中,amessage是一个仅仅足以存放初始化字符串以及空字符'\0'的一维数组。数组中的单个字符可以进行修改,但amessage始终指向同一存储位置。另一方面,pmessage是一个指针,其初值指向一个字符串常量,之后它可以修改以指向其他地址,但如果试图修改字符串得内容,结果是没有定义的(通常会触发段错误)。下面是两个关于字符数组作为参数的例子:

void strcpy(char *s,char *t){
    /* 没有考虑到s的空间不够的情况 */
    while(*s++ = *t++)
        ;
}

int strcmp(char *s,char *t){
    /* 这里的写法很值得学习 */
    for(;*s == *t;s++,t++){
        if(*s == '\0'){
            return 0;
        }
    }
    return *s - *t;
}

指针数组以及指向指针的指针

由于指针本身也是变量,所以它们也可以像其他变量一样存储在数组中。下面是一个例子:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXLINES 5000
#define MAXLEN 1000

char *lineptr[MAXLINES];
void quicksort(char *lineptr[],int left,int right);
void swap(char *v[],int i,int j){
    char *temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}

int main(int argc, char const *argv[])
{
    /* 把数据读到lineptr中 */
    int lines;
    char buf[MAXLEN];
    int len = 0;
    while(gets(buf) && len<MAXLINES){
        char *s = (char *)malloc(MAXLEN*sizeof(char));
        strcpy(s,buf);
        lineptr[len++] = s;
    }

    quicksort(lineptr,0,len-1);
    for(int i=0;i<len;i++){
        printf(lineptr[i]);
        free(lineptr[i]);
    }
    return 0;
}

void quicksort(char *lineptr[],int left,int right){
    /* 递归出口 */
    if(left >= right){
        return;
    }
    int pivot = (left + right)/2;
    swap(lineptr,pivot,left);
    int i,j;/* j指向第一个小于等于pivot的值 */
    for(i=left+1,j = left;i<=right;i++){
        if(strcmp(lineptr[i],lineptr[left])){
            swap(lineptr,++j,i);
        }
    }
    swap(lineptr,left,j);
    quicksort(lineptr,left,j-1);
    quicksort(lineptr,j+1,right);
}

这个程序是我根据书上的内容改的,可能有一些不好的地方,但是基本功能是实现了的。

多维数组

C语言提供了类似于矩阵的多维数组,但实际上它们并不像指针数组使用得那样广泛。下面以二维数组为例。多维数组的使用和一维数组类似:

int array[10][10];/* 声明一个二维数组 */
array[5][5] = 10; /* 对二维数组的元素赋值 */

值得注意的是,如果将二维数组作为参数传递给函数,那么在函数的参数声明中必须指明数组的列数,如:

void fun(int array[][10]);

该声明还可以写成:

void fun(int *array[10]);

命令行参数

在支持C语言的环境中,可以在程序开始执行时将命令行参数传递给程序。调用主函数main时,它带有两个两个参数。第一个参数(习惯上称argc,用于参数计数)的值表示运行程序时命令行中参数的数目。第二个参数(称为argv,用于参数向量)是一个指向字符串数组的指针,其中每一个字符串对应一个参数。下面是一个例子,打印出命令行中的参数:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int i;
    for(i=1;i<argc;i++){
        printf("%s%s",argv[i],i<argc-1?" ":"");
    }
    printf("\n");
    getchar();
    return 0;
}

指向函数的指针

在C语言中,函数本身不是变量,但可以定义指向函数的指针。这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等等。下面看一个例子:

/* 定义函数指针,该函数的返回值是int,参数为两个int */
typedef int (*compare)(int, int);

int max(int a, int b){
    if (a > b){
        return 1;
    }
    else if (a < b) {
        return -1;
    }
    else {
        return 0;
    }
}

int min(int a, int b){
    if (a < b) {
        return 1;
    }
    else if (a > b){
        return -1;
    }
    else{
        return 0;
    }
}

void swap(int *v, int x, int y){
    int temp = v[x];
    v[x] = v[y];
    v[y] = temp;
}

void quicksort(int *v, int left, int right, compare comp){
    /* 递归出口 */
    if (left >= right){
        return;
    }
    int i, j;
    for (i = left + 1, j = left; i <= right; i++){
        if (comp(v[i], v[left]) > 0){
            swap(v, ++j, i);
        }
    }
    swap(v, left, j);
    quicksort(v, left, j - 1, comp);
    quicksort(v, j + 1, right, comp);
}

编写完了之后能运行,但我发现函数调用的时候,书上写的是(*comp)(v[i],v[left]),但我发现像我上面那样写也是可以调用起来的。这可能是C编译器做的一些优化吧,从理解上来说,还是书上的比较好。

复杂声明

C语言中的声明有点令人难以理解,主要原因是它是从变量开始,然后按照运算符的优先顺序,进行左右结合。因为不是从左往右或从右往左,所以令人难以理解,下面是几个例子;

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

推荐阅读更多精彩内容

  • 目录 XML简介 XML基本语法 XML解析 * DOM解析   * DOM解析原理及工具   * DOM4J解析...
    望町阅读 657评论 0 2
  • 上一段感情是我一直不愿意提起的,它像黑夜,一想起来就会吞噬我,淹没我,让我好不容易砌成的自认为坚不可摧的城堡,轰然...
    与愁予阅读 535评论 3 4
  • [玫瑰]20170215徐海波读《不输在家庭教育上》分享(上海,第193天) 《读懂孩子说谎的“怪招”》摘录: “...
    觉之灯阅读 176评论 0 0
  • 习惯:学习1小时 结果:未达标 心情:还好 原因分析:主要时间花在了特卖发文 收获:完成了7篇图集,找了新的产品,...
    潮女搭配心经阅读 214评论 0 0
  • 风贯呼佳音 今手难持笛 初春江暖冬花惜 杨花拂乐律 指尖迟素琴 轻云更催细雨泣 何处驻足,踌躇归途 人久共剪西窗烛...
    夜流YELIU阅读 206评论 0 1