GCC下“initializing ‘char **’ with an expression of type ‘const char **’ discards qualifiers in nested pointer types”警告解读

我曾经被这个警告困扰了好久好久,问了好些人,都说没遇到过=_=…
问题代码片段(与问题无关的代码块已被忽略):

size_t MyConv(const char *orginStr, const char *targetStr, ...) {
    char **tmpIn = NULL;
    char **tmpOut = NULL;
        ...
        ...
    tmpIn = &orginStr;
    tmpOut = &targetStr;
        ...
        ...
}

GCC编译后会弹出如下警报:

sfind.c:887:8: warning: assigning to 'char **' from 'const char **' discards qualifiers in nested pointer types
      [-Wincompatible-pointer-types-discards-qualifiers]
        tmpIn = &orginStr;
              ^ ~~~~~~~~~

这条警告信息翻译出来是“用类型表达式初始化“char \**” “const char \**”丢弃嵌套指针类型中的限定符。 ”,个人表示很晦涩难懂。。。
后来翻阅《C专家编程》的时候,偶然间发现一个章节是讨论const限定符的,认真看了下,顿时感慨自己学识太过浅薄。
上面的代码中,实参const char \*orginStr的const是修饰char orginStr的,而char **tmpOut是指向char *类型变量的一个指针(此“char *”类型无修饰符),这两点倒是没有疑惑。但当二者互相赋值的时候,const char *orginStrchar \**tmpOut这两个指针所指向的类型的修饰符不同。一个是const char orginStr,带了const修饰符,而另一个则是char *tmpOut,并没有const修饰符。指针变量赋值时,赋值语句左边的指针所指向的类型必须具有赋值语句右边的类型所拥有的全部修饰符,才能正常通过编译,否则就会舍弃右边变量多余的修饰符。

以下内容摘自ANSI C标准手册

两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。

也就是说:

int *a = NULL;
const int *b = a;

上面这段代码是可以正常通过编译的,因为赋值语句左边的指针指向的数据类型拥有了右边的数据类型的所有修饰符(甚至还比右边的多了一个const)而下面这段却不行。

const int *a = NULL;
int b = a;

我对C语言中字符串指针的一个误解

在QQ群中看到一道C语言题:

定义char a[10]和char *p=a,下面的赋值语句中,正确的是___
A. a[10]=”Turbo C” B. p=”Turbo C”
C. a=”Turbo C” D. *p=”Turbo C”

这道题选B本身是没有任何值得疑惑的地方的,但是,且看我下面这个小程序。

#include<stdio.h>
int main(void) {
    char a[10];
    char *p = a;
    p = "Turbo C";
    printf("%s\n", a); //这里我打印的是数组a而不是指针p
    return 0;
}

在Linux下编译执行后输出乱码,这个错误是因为我一开始以为:p = “Turbo C”是将”Turbo C”这个字符串赋值给了p所指向的字符数组a。
实际上,p = “Turbo C”这条语句在执行时做了三件事。

  1. 在内存中创建了一个常量空间,用于存放字符串
  2. 在字符串末尾添加了一个/0
  3. 返回字符串在内存中存放位置的首地址给p指针

所以,p指针中所保存的内容已经由字符数组a的首地址变为了字符串”Turbo C”的首地址,想当然的,字符串”Turbo C”也并未被赋值给字符数组a,而是存在于新的内存空间中。
此时我再用printf输出字符数组a的内容就会打印出一些乱七八糟的东西啦——因为我没有给数组a初始化,只有天才晓得编译器在里面填了什么东东~

那么上面那道程序这样改一下就正常工作啦:
将第7行改为

printf("%s\n", p);