[英]Warning: comparison with string literals results in unspecified behaviour
我正在開始一個項目,為 C 中的 linux 編寫一個簡化的 shell。我對 C 和 Linux 都不精通,這正是我認為這是個好主意的原因。
從解析器開始,我已經遇到了一些問題。
代碼應該簡單明了,這就是為什么我沒有包含任何注釋。
我收到 gcc 的警告:“與字符串文字的比較會導致未指定的行為”,注釋為“此處警告”的行(請參見下面的代碼)。
我不知道為什么這會導致警告,但真正的問題是即使我將“<”與“<”進行比較也不會進入 if...
我正在尋找所解釋問題的答案,但是如果您在代碼中看到應該改進的地方,請說出來。 請記住,我不是那么精通,而且這仍在進行中(或者更好的是,開始工作)。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
typedef enum {false, true} bool;
typedef struct {
char **arg;
char *infile;
char *outfile;
int background;
} Command_Info;
int parse_cmd(char *cmd_line, Command_Info *cmd_info)
{
char *arg;
char *args[100];
int i = 0;
arg = strtok(cmd_line, " \n");
while (arg != NULL) {
args[i] = arg;
arg = strtok(NULL, " \n");
i++;
}
int num_elems = i;
cmd_info->infile = NULL;
cmd_info->outfile = NULL;
cmd_info->background = 0;
int iarg = 0;
for (i = 0; i < num_elems; i++)
{
if (args[i] == "&") //WARNING HERE
return -1;
else if (args[i] == "<") //WARNING HERE
if (args[i+1] != NULL)
cmd_info->infile = args[i+1];
else
return -1;
else if (args[i] == ">") //WARNING HERE
if (args[i+1] != NULL)
cmd_info->outfile = args[i+1];
else
return -1;
else
cmd_info->arg[iarg++] = args[i];
}
cmd_info->arg[iarg] = NULL;
return 0;
}
void print_cmd(Command_Info *cmd_info)
{
int i;
for (i = 0; cmd_info->arg[i] != NULL; i++)
printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);
printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);
printf("infile=\"%s\"\n", cmd_info->infile);
printf("outfile=\"%s\"\n", cmd_info->outfile);
printf("background=\"%d\"\n", cmd_info->background);
}
int main(int argc, char* argv[])
{
char cmd_line[100];
Command_Info cmd_info;
printf(">>> ");
fgets(cmd_line, 100, stdin);
parse_cmd(cmd_line, &cmd_info);
print_cmd(&cmd_info);
return 0;
}
您想使用strcmp() == 0
來比較字符串而不是簡單的==
,它只會比較指針是否相同(在這種情況下它們不會相同)。
args[i]
是指向字符串的指針(指向以空字符結尾的字符數組的指針),就像"&"
或"<"
。
表達式argc[i] == "&"
檢查兩個指針是否相同(指向相同的內存位置)。
表達式strcmp( argc[i], "&") == 0
將檢查兩個字符串的內容是否相同。
'a'
和"a"
之間有區別:
'a'
表示字符a
的值。"a"
表示存儲字符串"a"
的內存位置的地址(通常位於程序內存空間的數據部分)。 在那個內存位置,您將有兩個字節——字符'a'
和字符串的空終止符。if (args[i] == "&")
好的,讓我們分析一下這是做什么的。
args 是一個指針數組。 因此,在這里您將args[i]
(一個指針)與"&"
(也是一個指針)進行比較。 好吧,這一切都成立的唯一方法是,如果您在某個地方有args[i]="&"
並且即使如此, "&"
也不能保證在任何地方都指向同一個地方。
我相信您實際上正在尋找的是strcmp
來比較整個字符串,或者您想要做if (*args[i] == '&')
將args[i]
字符串的第一個字符與&
字符進行比較
clang在錯誤報告和恢復方面具有優勢。
$ clang errors.c errors.c:36:21: warning: result of comparison against a string literal is unspecified (use strcmp instead) if (args[i] == "&") //WARNING HERE ^~ ~~~ strcmp( , ) == 0 errors.c:38:26: warning: result of comparison against a string literal is unspecified (use strcmp instead) else if (args[i] == "<") //WARNING HERE ^~ ~~~ strcmp( , ) == 0 errors.c:44:26: warning: result of comparison against a string literal is unspecified (use strcmp instead) else if (args[i] == ">") //WARNING HERE ^~ ~~~ strcmp( , ) == 0
它建議用strcmp(x,y) == 0
替換x == y
。
gengetopt為您編寫命令行選項解析器。
這是一個老問題,但我最近不得不向某人解釋它,我認為在這里記錄答案至少有助於理解 C 的工作原理。
字符串文字如
"a"
或者
"This is a string"
放在程序的文本或數據段中。
C 中的字符串實際上是一個指向字符的指針,該字符串被理解為內存中的后續字符,直到遇到 NUL 字符為止。 也就是說,C 並不真正了解字符串。
所以如果我有
char *s1 = "This is a string";
那么 s1 是指向字符串第一個字節的指針。
現在,如果我有
char *s2 = "This is a string";
這也是指向程序文本或數據段中該字符串的相同第一個字節的指針。
但如果我有
char *s3 = malloc( 17 );
strcpy(s3, "This is a string");
然后 s3 是指向內存中另一個位置的指針,我將其他字符串的所有字節復制到其中。
示例:
雖然,正如您的編譯器正確指出的那樣,您不應該這樣做,但以下內容將評估為真:
s1 == s2 // True: we are comparing two pointers that contain the same address
但以下將評估為假
s1 == s3 // False: Comparing two pointers that don't hold the same address.
盡管擁有這樣的東西可能很誘人:
struct Vehicle{
char *type;
// other stuff
}
if( type == "Car" )
//blah1
else if( type == "Motorcycle )
//blah2
你不應該這樣做,因為它不是保證工作的東西。 即使您知道該類型將始終使用字符串文字進行設置。
我已經測試過它並且它有效。 如果我做
A.type = "Car";
然后 blah1 被執行,對於“摩托車”也是如此。 你可以做這樣的事情
if( A.type == B.type )
但這太可怕了。 我寫它是因為我認為知道它為什么起作用很有趣,它有助於理解為什么你不應該這樣做。
解決方案:
在您的情況下,您想要做的是使用strcmp(a,b) == 0
替換a == b
在我的示例中,您應該使用枚舉。
enum type {CAR = 0, MOTORCYCLE = 1}
前面的字符串很有用,因為你可以打印類型,所以你可能有一個這樣的數組
char *types[] = {"Car", "Motorcycle"};
現在我考慮了一下,這很容易出錯,因為必須小心地在 types 數組中保持相同的順序。
因此最好這樣做
char *getTypeString(int type)
{
switch(type)
case CAR: return "Car";
case MOTORCYCLE: return "Motorcycle"
default: return NULL;
}
我今天在使用客戶程序時遇到了這個問題。 該程序在 VS6.0 中運行良好,使用以下內容:(我稍微更改了它)
//
// This is the one include file that every user-written Nextest programs needs.
// Patcom-generated files will also look for this file.
//
#include "stdio.h"
#define IS_NONE( a_key ) ( ( a_key == "none" || a_key == "N/A" ) ? TRUE : FALSE )
//
// Note in my environment we have output() which is printf which adds /n at the end
//
main {
char *psNameNone = "none";
char *psNameNA = "N/A";
char *psNameCAT = "CAT";
if (IS_NONE(psNameNone) ) {
output("psNameNone Matches NONE");
output("%s psNameNoneAddr 0x%x \"none\" addr 0x%X",
psNameNone,psNameNone,
"none");
} else {
output("psNameNone Does Not Match None");
output("%s psNameNoneAddr 0x%x \"none\" addr 0x%X",
psNameNone,psNameNone,
"none");
}
if (IS_NONE(psNameNA) ) {
output("psNameNA Matches N/A");
output("%s psNameNA 0x%x \"N/A\" addr 0x%X",
psNameNA,psNameNA,
"N/A");
} else {
output("psNameNone Does Not Match N/A");
output("%s psNameNA 0x%x \"N/A\" addr 0x%X",
psNameNA,psNameNA,
"N/A");
}
if (IS_NONE(psNameCAT)) {
output("psNameNA Matches CAT");
output("%s psNameNA 0x%x \"CAT\" addr 0x%X",
psNameNone,psNameNone,
"CAT");
} else {
output("psNameNA does not match CAT");
output("%s psNameNA 0x%x \"CAT\" addr 0x%X",
psNameNone,psNameNone,
"CAT");
}
}
如果在 VS6.0 中內置帶有編輯並繼續的程序數據庫。 比較似乎可以工作。 使用此設置啟用 STRING 池,並且編譯器優化所有 STRING 指針指向相同地址,因此這可以工作。 編譯后即時創建的任何字符串都將具有不同的地址,因此比較將失敗。 將設置更改為 Program Database only 將構建程序,因此它會失敗。
或者可能是這樣的:
bool stringEquals(const char *__lhs, const char *__rhs) {
return strcmp(__lhs, __rhs) == 0;
}
出於可讀性原因...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.