《C程序设计语言》学习笔记 -- 指针部分


指针与地址

指针是能够存放一个地址的一组存储单元(通常是2个或4个字节)。
一元运算符&可用于取一个对象的地址,如p = &c; 表示将c的地址赋值给变量p,称p为“指向”c的指针。地址运算符&只能应用于内存中的对象,即变量与数组元素。
一元运算符*是间接寻址或间接引用运算符。当它作用于指针时,将访问指针所指向的对象。

int x = 1, y = 2, z[10];
int *ip;          /* ip is a pointer to int */
ip = &x;          /* ip now points to x */
y = *ip;          /* y is now 1 */
*ip = 0;          /* x is now 0 */
ip = &z[0];       /* ip now points to z[0] */
++*ip或(*ip)++    /* (*ip)++ 中的()是必需的,否则,该表达式将对ip进行加1运算,而不是对ip指向的对象进行加1运算,这是因为类似于*和++这样的运算符遵循从右至左的结合顺序 */
iq = ip           /* 若iq是另一个指向整型的指针,则将ip中的值拷贝到iq中,这样,指针iq也将指向ip指向的对象 */
// 由于指针也是变量,所以在程序中可以直接使用,而不必通过间接引用的方法使用。

指针与函数参数

由于C语言是以值传递的方式将参数值传给被调用函数,因此,被调用函数不能直接修改主调函数中变量的值。
指针参数使得被调用函数能够访问和修改主调函数中对象的值。

// swap()的所有参数都声明为指针,并且通过这些指针来间接访问它们指向的操作数
void swap(int *px, int *py) {
    int temp = *px;
    *px = *py;
    *py = temp;
}
// 使主调程序将所要交换的变量的指针传递给被调用函数
swap(&a, &b);

*语法糖*
// 由于一元运算符&用来取变量的地址,这样 &a 就是一个指向变量a的指针
void swap(int &a, int &b) {
    int t = a;
    a = b;
    b = t;
}
// main()调用
swap(a, b);

指针与数组

通过数组下标能完成的任何操作都可以通过指针来实现。
但数组名和指针有一个不同之处,指针是一个变量,但数组名不是。所以,语句pa = a和pa++都是合法的;类似于a = pa和a++形式的语句是非法的。

int a[10];  // 定义一个长度为10的数组
int *pa;    // 声明pa为指向整型对象的指针
pa = &a[0]; // 将指针pa指向数组a的第0个元素,即pa的值为数组元素a[0]的地址
pa = a;     // 因为数组名所代表的就是该数组最开始的一个元素的地址,通常取代pa = &a[0];
x = *pa;    // 将数组元素a[0]的内容复制到变量x中
pa[i]与*(pa+i)是等价的  // 一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现

对数组元素a[i]的引用也可以写成*(a+i) 
&a[i]和a+i的含义相同【&a[0]等价于a】 // a+i是a之后第i个元素的地址

【若指针pa指向a[0],则*(pa+1)引用的是a[1]的内容,
                  pa+i是a[i]的地址,*(pa+i)引用的是a[i]的内容
“指针加1”意味着pa+1指向pa所指向的对象的下一个对象;pa+i指向pa所指向的对象之后的第i个对象】

当把数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址。
在被调用函数中,该参数是一个局部变量,因此数组名参数必须是一个指针,也就是一个存储地址值的变量
/* strlen:  return length of string s */
   int strlen(char *s) {
       int n;
       for (n = 0; *s != '\0', s++) {
           n++;
       }
       return n;
   }
// 因为s是一个指针,所以对其执行自增运算是合法的。执行s++运算不会影响到strlen函数的调用者中的字符串,它仅对该指针在strlen函数中的私有副本进行自增运算

地址算数运算

指针算术运算的特点:
1. 在某些情况下对指针可以进行比较运算
指针p和q指向同一个数组的成员,若p指向的数组元素的位置在q指向的数组元素位置之前,则关系表达式
    p < q 
的值为真
2. 指针可以和整数进行相加或相减运算
(1) 结构 p + n 表示指针p当前指向的对象之后第n个对象的地址
在计算p+n时,n根据p指向的对象的长度按比例缩放(p指向的对象的长度取决于p的声明)
(2) 如果指针p和q指向同一个数组中的元素,且p < q,
那么 q - p + 1 就是位于p和q指向的元素之间的元素的数目。
/* strlen:  return length of string s */
int strlen(char *s) {
    char *p = s;
    while (*p != '\0') {
        p++;
    }
    return p - s;
}
(3) 指针的算术运算具有一致性 ???留疑

有效的指针运算包括同类型指针之间的赋值运算;
               指针同整数之间的加法或减法运算;
               指向相同数组中元素的两个指针间的减法或比较运算;
               将指针赋值为0或指针与0之间的比较运算。
其他所有形式的指针运算都是非法的

字符指针与函数

字符串常量是一个字符数组,例如:"I am a string"。在字符串的内部表示中,字符数组以'\0'结尾,所以,程序可以通过检查空字符找到字符数组的结尾。字符串常量占据的存储单元数比双引号内的字符数大1。

字符串常量的用法:
1. 作为函数参数
printf("Hello world\n");
当类似于这样一个字符串出现在程序中时,实际上是通过字符指针访问该字符串的。
在上述语句中,printf接受的是一个指向字符数组第一个字符的指针,即字符串常量可通过一个指向其第一个元素的指针访问




由于++和--既可以作为前缀运算符,也可以作为后缀运算符,所以还可以将运算符*与++和--按照
其他方式组合使用。例如,表达式 *--p 在读取指针p指向的字符之前先对执行自减运算。
// 下面两个表达式是进栈和出栈的标准用法
*p++ = val;  /* 将val压入栈 */
val = *--p;  /* 将栈顶元素弹到val中 */

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

由于指针也是变量,所以它们也可以像其他变量一样存储在数组中。

使用场景:如编写程序按字母顺序对由文本行组成的集合进行排序【每行文本行长度不一】(与整数不同的是,它们不能在单个运算中完成比较或移动操作)。需要一个能够高效,方便地处理可变长度文本行的数据表示方法。

这种实现方法消除了因移动文本行本身所带来的复杂的存储管理和巨大的开销这两个孪生问题。

</br>

多维数组

C语言提供了类似于矩阵的多维数组,但实际上它们并不像指针数组使用得那么广泛。

使用场景:如日期转换问题。
static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
/* day_of_year:  set day of year from month & day */
int day_of_year(int year, int month, int day) {
    int i, leap;
    leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    for (i = 1; i < month; i++)
        day += daytab[leap][i];
    return day;
}
/* month_day:  set month, day from day of year */
void month_day(int year, int yearday, int *pmonth, int *pday) {
    int i, leap;
    leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    for (i = 1; yearday > daytab[leap][i]; i++)
        yearday -= daytab[leap][i];
    *pmonth = i;
    *pday = yearday;
}

</br>

指针数组的初始化


</br>

指针与多维数组

二维数组与指针数组之间的区别
假如有以下两个定义:

int a[10][20];
int *b[10];

指针数组最频繁的用处是存放具有不同长度的字符串。
以函数month_name中的情况为例作比较:

1. 指针数组的声明和图形化描述
char *name[]={"Illegal manth", "Jan", "Feb", "Mar"};

2. 二维数组的声明和图形化描述
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };


命令行参数

指向函数的指针,复杂声明等章节略过

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

推荐阅读更多精彩内容