我正在研究一组 C 代码来优化它。我在修复损坏的代码时遇到了警告。
环境是Linux,C99,用-Wall -O2
flags编译。
最初的结构文本定义如下:
struct text {
char count[2];
char head[5];
char textdata[5];
}
代码是返回指针T1
和T2
预期head
和textdata
字符串:
int main(void) {
struct text *T1;
char *T2;
char data[] = "02abcdeabcde";
T1 = (struct text *)data;
T2 = T1->textdata;
gettextptr((char *)T1, T2);
printf("\nT1 = %s\nT2 = %s\n", (char *)T1, T2);
return (0);
}
void gettextptr(char *T1, char *T2) {
struct text *p;
int count;
p = (struct text *)T1;
count = (p->count[0] - '0') * 10 + (p->count[1] - '0');
while (count--) {
if (memcmp(T2, T1, 2) == 0) {
T1 += 2;
T2 += 2;
}
}
}
这没有按预期工作。预计会返回第一个“c”和最后一个“e”的地址。通过GDB,我发现,一旦从执行指针返回gettextptr()
到父函数,它不保留的地址T1
和T2
。然后我尝试了另一种使用双指针“按引用调用”的方法:
int main(void) {
struct text *T1;
char *T2;
char data[] = "02abcdeabcde";
T1 = (struct text *)data;
T2 = T1->textdata;
gettextptr((char **)&T1, &T2);
printf("\nT1 = %s\nT2 = %s\n", (char *)T1, T2);
return (0);
}
void gettextptr(char **T1, char **T2) {
struct text *p;
int count;
p = (struct text *)(*T1);
count = (p->count[0] - '0') * 10 + (p->count[1] - '0');
while (count--) {
if (memcmp(*T2, *T1, 2) == 0) {
*T1 += 2;
*T2 += 2;
}
}
}
当我使用 编译此代码时-Wall -O2
,我收到以下 GCC 警告:
pointer.c: In function ‘main’:
pointer.c:23: warning: dereferencing type-punned pointer will break strict-aliasing rules
所以:
在第一种情况下,代码是按值调用的吗?
是不是(char **)
允许,同时保持严格别名规则铸造?
我缺少什么来解决此警告?
严格的别名规则是标准的第 6.5/7 段。它基本上说你只能通过兼容类型的左值访问一个对象,可能还有额外的限定符;相应的有符号/无符号类型;数组、结构或联合类型及其成员之一,或字符类型。您收到的诊断报告说您的代码违反了该规则,并且多次违反了该规则。
你很早就陷入困境:
T1 = (struct text *)data;
这种转换是允许的,虽然不能保证得到的指针正确对齐,但在T1
不违反严格的别名规则的情况下,你无能为力。特别是,如果你用*
or取消引用它->
——这实际上是你接下来要做的事情——那么你访问一个char
数组就好像它是一个struct text
. 这是不允许的,尽管反过来会是另一回事。
转换T1
为 achar *
并通过该指针访问指向的数组,正如您稍后所做的那样,是您可以用它做的一些事情。
gettextexpr()
是相同的(两个版本)。它执行与上述相同类型的转换,并在访问 时取消引用转换后的指针p->count
。由此产生的行为违反了严格的别名规则,因此是未定义的。然而,GCC 在第二种情况下实际上抱怨的可能是访问*T1
好像它是一个char *
,而实际上它是struct text *
另一个,单独的,严格的别名违规。
因此,针对您的具体问题:
- 在第一种情况下,代码是按值调用的吗?
C只有按值传递,所以是的。在第一种情况下,您char
按值传递两个指针,然后您可以使用它们来修改调用者的char
数据。在第二种情况下,您char *
按值传递两个指针,您可以并且确实使用它们来修改调用者的char *
变量。
- 在保持严格的别名规则的同时,是否允许 (char **) 进行转换?
不,绝对不是。强制转换为 char *
(not char **
) 可以允许您通过结果指针访问对象的表示,因为取消引用 achar *
会产生字符类型的左值,但没有任何类型可以在没有严格别名含义的情况下进行一般转换。
- 我缺少什么来解决此警告?
你错过了你试图做的事情从根本上是不允许的。C 不允许像访问, 句点char
一样访问数组struct text
。尽管如此,编译器可能会接受这样做的代码,但其行为是未定义的。
通过放弃强制转换结构方法来解决警告,无论如何,这种方法只提供语法糖粉。摆脱所有的铸造并编写实际上更简单和更清晰:
count = ((*T1)[0] - '0') * 10 + ((*T1)[1] - '0');
摆脱所有的铸造用途也许更清楚sscanf
:
sscanf(*T1, "%2d", &count);
另请注意,即使允许,您的特定访问模式似乎对结构成员的布局做出了不由语言证明的假设。实现可能会在成员之间和最后一个成员之后使用任意填充,而您的代码无法适应这种情况。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句