奇怪的行为删除 C 字符串中的重复字符
Odd behavior removing duplicate characters in a C string
我在用于简单的基于替换的加密的程序中使用以下方法。该方法专门用于去除encryption/decryption键中的重复字符。
该方法和程序的其余部分一样有效,它适用于我试过的 99% 的键。但是,当我将键 "goodmorning"
或任何由相同字母以任何顺序组成的键(例如 "dggimnnooor"
)传递给它时,它会失败。此外,字符数多于 "goodmorning"
的键以及字符数少于
的键都有效。
我 运行 通过 lldb
使用相同参数的可执行文件,它可以工作。我已经在机器 运行 CentOS 上克隆了我的存储库,它按原样工作。
但是我在编译时没有收到任何警告或错误。
//setting the key in main method
char * key;
key = removeDuplicates(argv[2]);
//return 1 if char in word
int targetFound(char * charArr, int num, char target){
int found = 0;
if(strchr(charArr,target))
found = 1;
return found;
}
//remove duplicate chars
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len * sizeof(char));
if (result == NULL)
errorHandler(2);
char ch;
int i;
int j;
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
}
}
return result;
}
每个请求:如果将 "feather"
传递给此函数,则结果字符串将为 "feathr"
.
我在您的代码中发现了几个问题:
- 您没有用空字符终止输出。
- 当输入中没有重复字符时,您没有分配足够的内存来保存空字符。
因此,您的程序具有未定义的行为。
改变
result = malloc (len * sizeof(char));
至
result = malloc (len+1); // No need for sizeof(char)
函数前添加如下内容returns.
result[j] = '[=12=]';
另一个主要问题是您在 result
上使用 strchr
,当您调用 targetFound
时它不是空终止字符串。这也导致了未定义的行为。您需要使用:
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len+1);
if (result == NULL)
{
errorHandler(2);
}
char ch;
int i;
int j;
// Make result an empty string.
result[0] = '[=13=]';
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
// Null terminate again so that next call to targetFound()
// will work.
result[j] = '[=13=]';
}
}
return result;
}
第二种选择是不在 targetFound
中使用 strchr
。请改用 num
并实现等效功能。
int targetFound(char * charArr, int num, char target)
{
for ( int i = 0; i < num; ++i )
{
if ( charArr[i] == target )
{
return 1;
}
}
return 0;
}
这将使您避免多次将空字符分配给 result
。您只需要在最后终止 result
。
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len+1);
if (result == NULL)
{
errorHandler(2);
}
char ch;
int i;
int j;
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
}
}
result[j] = '[=15=]';
return result;
}
As R Sahu already said, you are not terminating your string with a NUL
character. Now I'm not going to explain why you need to do this, but you always need to terminate your strings with a NUL
character, which is '[=13=]'
. If you want to know why, head over here 以获得很好的解释。然而,这不是您的代码的唯一问题。
主要问题是您调用的函数 strchr
是为了查明您的 result
是否已经包含某些字符 希望您传递一个 NUL
终止字符串,但你的变量没有NUL
终止,因为你不断向它附加字符。
为了解决您的问题,我建议您改用地图。映射您已经使用的所有字符,如果它们不在地图中,则将它们同时添加到地图和结果中。这更简单(不需要调用 strchr
或任何其他函数),更快(不需要每次都扫描所有字符串),最重要的是正确。
这是一个简单的解决方案:
char *removeDuplicates(char *word){
char *result, *map, ch;
int i, j;
map = calloc(256, 1);
if (map == NULL)
// Maybe you want some other number here?
errorHandler(2);
// Add one char for the NUL terminator:
result = malloc(strlen(word) + 1);
if (result == NULL)
errorHandler(2);
for(i = 0, j = 0; word[i] != '[=10=]'; i++) {
ch = word[i];
// Check if you already saw this character:
if(map[(size_t)ch] == 0) {
// If not, add it to the map:
map[(size_t)ch] = 1;
// And to your result string:
result[j] = ch;
j++;
}
}
// Correctly NUL terminate the new string;
result[j] = '[=10=]';
return result;
}
为什么这在其他机器上可以,但在你的机器上不行?
您是未定义行为的受害者。不同系统上的不同编译器以不同方式处理未定义的行为。例如,GCC 可能决定在这种特殊情况下不做任何事情,并使 strchr
继续在内存中搜索,直到找到 '[=13=]'
字符,而这正是发生的情况。您的程序一直在搜索 NUL
终止符并且永不停止,因为谁知道 '[=13=]'
可能在您的字符串之后的内存位置?这既危险又不正确,因为程序没有读取为它保留的内存,例如,另一个编译器可能决定在那里停止搜索,并给你一个正确的结果。然而,这不是理所当然的事情,您应该始终避免未定义的行为。
我在用于简单的基于替换的加密的程序中使用以下方法。该方法专门用于去除encryption/decryption键中的重复字符。
该方法和程序的其余部分一样有效,它适用于我试过的 99% 的键。但是,当我将键 "goodmorning"
或任何由相同字母以任何顺序组成的键(例如 "dggimnnooor"
)传递给它时,它会失败。此外,字符数多于 "goodmorning"
的键以及字符数少于
我 运行 通过 lldb
使用相同参数的可执行文件,它可以工作。我已经在机器 运行 CentOS 上克隆了我的存储库,它按原样工作。
但是我在编译时没有收到任何警告或错误。
//setting the key in main method
char * key;
key = removeDuplicates(argv[2]);
//return 1 if char in word
int targetFound(char * charArr, int num, char target){
int found = 0;
if(strchr(charArr,target))
found = 1;
return found;
}
//remove duplicate chars
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len * sizeof(char));
if (result == NULL)
errorHandler(2);
char ch;
int i;
int j;
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
}
}
return result;
}
每个请求:如果将 "feather"
传递给此函数,则结果字符串将为 "feathr"
.
我在您的代码中发现了几个问题:
- 您没有用空字符终止输出。
- 当输入中没有重复字符时,您没有分配足够的内存来保存空字符。
因此,您的程序具有未定义的行为。
改变
result = malloc (len * sizeof(char));
至
result = malloc (len+1); // No need for sizeof(char)
函数前添加如下内容returns.
result[j] = '[=12=]';
另一个主要问题是您在 result
上使用 strchr
,当您调用 targetFound
时它不是空终止字符串。这也导致了未定义的行为。您需要使用:
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len+1);
if (result == NULL)
{
errorHandler(2);
}
char ch;
int i;
int j;
// Make result an empty string.
result[0] = '[=13=]';
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
// Null terminate again so that next call to targetFound()
// will work.
result[j] = '[=13=]';
}
}
return result;
}
第二种选择是不在 targetFound
中使用 strchr
。请改用 num
并实现等效功能。
int targetFound(char * charArr, int num, char target)
{
for ( int i = 0; i < num; ++i )
{
if ( charArr[i] == target )
{
return 1;
}
}
return 0;
}
这将使您避免多次将空字符分配给 result
。您只需要在最后终止 result
。
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len+1);
if (result == NULL)
{
errorHandler(2);
}
char ch;
int i;
int j;
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
}
}
result[j] = '[=15=]';
return result;
}
As R Sahu already said, you are not terminating your string with a NUL
character. Now I'm not going to explain why you need to do this, but you always need to terminate your strings with a NUL
character, which is '[=13=]'
. If you want to know why, head over here 以获得很好的解释。然而,这不是您的代码的唯一问题。
主要问题是您调用的函数 strchr
是为了查明您的 result
是否已经包含某些字符 希望您传递一个 NUL
终止字符串,但你的变量没有NUL
终止,因为你不断向它附加字符。
为了解决您的问题,我建议您改用地图。映射您已经使用的所有字符,如果它们不在地图中,则将它们同时添加到地图和结果中。这更简单(不需要调用 strchr
或任何其他函数),更快(不需要每次都扫描所有字符串),最重要的是正确。
这是一个简单的解决方案:
char *removeDuplicates(char *word){
char *result, *map, ch;
int i, j;
map = calloc(256, 1);
if (map == NULL)
// Maybe you want some other number here?
errorHandler(2);
// Add one char for the NUL terminator:
result = malloc(strlen(word) + 1);
if (result == NULL)
errorHandler(2);
for(i = 0, j = 0; word[i] != '[=10=]'; i++) {
ch = word[i];
// Check if you already saw this character:
if(map[(size_t)ch] == 0) {
// If not, add it to the map:
map[(size_t)ch] = 1;
// And to your result string:
result[j] = ch;
j++;
}
}
// Correctly NUL terminate the new string;
result[j] = '[=10=]';
return result;
}
为什么这在其他机器上可以,但在你的机器上不行?
您是未定义行为的受害者。不同系统上的不同编译器以不同方式处理未定义的行为。例如,GCC 可能决定在这种特殊情况下不做任何事情,并使 strchr
继续在内存中搜索,直到找到 '[=13=]'
字符,而这正是发生的情况。您的程序一直在搜索 NUL
终止符并且永不停止,因为谁知道 '[=13=]'
可能在您的字符串之后的内存位置?这既危险又不正确,因为程序没有读取为它保留的内存,例如,另一个编译器可能决定在那里停止搜索,并给你一个正确的结果。然而,这不是理所当然的事情,您应该始终避免未定义的行为。