【问题标题】:Building strcat without the library and without pointers在没有库和指针的情况下构建 strcat
【发布时间】:2019-01-26 12:30:57
【问题描述】:

我被要求在不使用库和指针的情况下从 构建

到目前为止我有这个,但不知何故它不起作用:

void strcatO(char a[], char b[])
{
    int i = 0;

    for(i = 0; i < strlen(b); ++i)
    {
        a[strlen(a) + i + 1] = b[i];
    }

    printf("%s", a);
}

输出:

【问题讨论】:

  • 请注意,当您声明像char a[] 这样的参数时,编译器真正将其视为char *a。所以不,没有指针你就不会这样做。
  • 猜你是对的
  • strlen() 来自图书馆。 ab 确实都是指针,即使它们的声明看起来像数组。实际上,如果你的老师告诉你不是,请他解释为什么你的strcat() 中的sizeof( a )总是等于sizeof( char * ),无论 调用函数是。归根结底,由于您用作函数调用的参数的 任何 数组 退化为指针,因此您的老师对他/她的学生进行了这样的表述任务,这是一种伤害。答案是,“这做不到,我能做的就是在没有* 的情况下编写它。”
  • 我事先在另一个函数中构建了 strlen,现在我得到了指针,谢谢!!
  • @PeterA.Schneider:在void f(int arr[5]); 中,不清楚数组长度是否被忽略。一些编译器评估数组大小表达式(之后丢弃该值)。例如,对于 Apple LLVM 10.0.0 clang-1000.11.45.5,void f(int arr[printf("Hello, world.\n")]) {} int main(void) { f(0); } 打印“Hello, world”。

标签: strcat string.h c strcat string.h


【解决方案1】:

不知怎的,它不起作用

a[strlen(a) + i + 1] = b[i]; 追加字符 a空字符

void strcatO(char a[], char b[]) {
    int i = 0;
    for(i = 0; i < strlen(b); ++i) {
      a[strlen(a) + i + 1] = b[i];  // Oops: appending position is off-by-one
    }
    printf("%s", a);
}

strcatO("ab", "cd") 将填充 a'a''b''\0''c''d'

使用printf("%s", a); 打印只会打印'a''b'


要修复,代码需要附加到正确的位置,但这会覆盖原来的a空字符。因此对strlen(a) 的调用是错误的。

相反,对于improve efficiency,不要重复调用strlen()

void strcatO(char a[], const char b[]) {
  size_t ai = 0;
  while (a[ai]) {       // go to end of a
    ai++;
  }

  size_t bi = 0;
  while (b[bi]) {        // while not at the end of b ...
    a[ai++] = b[bi++];
  }

  a[ai] = '\0';
  printf("<%s>", a);
}

细微改进的细节:

const char b[] 中的const 暗示b 引用此函数不应尝试更改的数据。这 1) 允许此函数连接 b 如果它是 const char [] 2) 允许弱编译器可能看不到的优化。

size_t 对于可能比INT_MAX 更长的long 字符串优于intsize_t 是字符串长度和数组大小的“正确大小”类型。 OP(原始海报)确实有“不使用库”并且size_t 来自库,因此代码可以使用unsigned 或更好的unsigned long 作为替代方案。

【讨论】:

  • 我认为 OP 写了一个 strlen 函数 ;-)。
  • @PeterA.Schneider 是的,OP 计算了 b 的长度,但丢失了它^^ 顺便说一句,OP 是什么意思?我使用它是因为有些人这样做,但不知道这些首字母缩略词的确切含义^^
  • @bruno:“原始海报”。
  • @bruno 无论是原创帖子还是原创海报,据我所知,至少我是这样使用的。
  • 顺便说一句,我喜欢这个答案,因为它把问题解释得很清楚,而且解决方案简洁正确。
【解决方案2】:

出于你的问题,你不断地计算 strlen 希望编译器会优化,你可以这样做:

#include <stdio.h>
#include <string.h>

void strcatO(char a[], char b[])
{
   size_t i = strlen(a);
   size_t j;

    for (j = 0; b[j] != 0; ++j)
    {
        a[i++] = b[j];
    }

    a[i] = 0;

    printf("%s\n", a);
}

int main()
{
  char a[20] = "aze";
  char b[] = "rtyu";
  strcatO(a,b);
  return 0;
}

执行:

azertyu

请注意,参数的char a[] 正好是char *没有指针 是假的;-)


并按照 Eric Postpischil 的要求指出代码中的问题:

  • a[strlen(a) + i + 1] 在正确位置后写 1 个字符,必须是 a[strlen(a) + 1] = 0; a[strlen(a)] = b[j];。在某种程度上,你可能会在结束后写得更远,因为 strlen 不会返回 a 的初始长度,而是一个未定义的值,因为可能缺少 null a 其余部分中的字符
  • 在你错过的副本之后添加空字符

【讨论】:

  • 你的回答太棒了,谢谢!!还没有了解 size_t 所以我不太明白,但我确实明白了你所做的谢谢
  • @LidorCohen size_tstrlen等函数返回值的类型,看stackoverflow.com/questions/2550774/what-is-size-t-in-c
  • 这个答案没有说明原始代码有什么问题或为什么新代码修复它。
  • @EricPostpischil 我做了,但最初的代码太糟糕了,我认为没用 ^^
  • @EricPostpischil 你是对的,我倾向于从某个级别的问题中考虑“隐藏我看不到的代码”,给出其他代码。
【解决方案3】:

这一行:

a[strlen(a) + i + 1] = b[i];

将字符写入比您想要的位置更远的位置。

在您的示例中调用时,您的例程将通过 ab 传递这些内容:

a[0] = 'e'
a[1] = 'g'
a[2] = 'g'
a[3] = 0

b[0] = 's'
b[1] = 'a'
b[2] = 'm'
b[3] = 'p'
b[4] = 'l'
b[5] = 'e'
b[6] = 0

你想产生这个结果:

a[0] = 'e'
a[1] = 'g'
a[2] = 'g'
a[3] = 's'
a[4] = 'a'
a[5] = 'm'
a[6] = 'p'
a[7] = 'l'
a[8] = 'e'
a[9] = 0

但是,由于您的代码写入a[strlen(a) + i + 1],它会将第一个字符写入a[strlen(a) + 0 + 1],即a[4]。你想要它在a[3]。您可以将strlen(a) + i + 1 更改为strlen(a) + i,但是当您写入第一个字符时,您将覆盖空终止字符,并且strlen 将无法再找到长度。要解决此问题,您可以在进入循环之前记住a 的长度。考虑这段代码:

int i = 0;
int LengthOfA = strlen(a);
for (i = 0; i < strlen(b); ++i)
{
    a[LengthOfA + i] = b[i];
}

这会将字符写入正确的位置。

但是,它不会在a 的末尾添加空终止字符。为此,我们可以在循环之后添加另一个语句:

a[LengthOfA + i] = 0;

此时,您的例行程序将适用于正常情况。不过,我们还可以进行另外两项改进。

首先,我们可以使用size_t,而不是使用int 来表示长度和索引。在 C 中,int 的宽度是灵活的,size_t 是一个很好的类型,可以在处理对象大小时使用。要使用它,首先使用#include &lt;stddef.h&gt; 来获取它的定义。那么你的代码可以是:

size_t i = 0;
size_t LengthOfA = strlen(a);
for (i = 0; i < strlen(b); ++i)
{
    a[LengthOfA + i] = b[i];
}
a[LengthOfA + i] = 0;

其次,您的代码名义上在每次迭代中都会计算 strlen(b)。这是浪费。最好计算一次长度并记住:

size_t i = 0;
size_t LengthOfA = strlen(a);
size_t LengthOfB = strlen(b);
for (i = 0; i < LengthOfB; ++i)
{
    a[LengthOfA + i] = b[i];
}
a[LengthOfA + i] = 0;

【讨论】:

    【解决方案4】:

    您没有覆盖第一个字符串 null(\0) 终止符

        a[strlen(a) + i + 1] = b[i];
    

    应该是

    int len = strlen(a);
    
    for(i = 0; i < strlen(b); ++i)
    {
        a[len + i] = b[i];
    }
    a[len+i] = '\0'; //Finally null terminate the new string.
    

    【讨论】:

    • !a 这是新的输出,数组'a'的总大小是25
    • 鉴于 OP 编写了自己的 strlen(),此代码遭受相同的 O(N*N) inefficiency。无需重复调用strlen(b)
    • 这个答案没有说明原始代码有什么问题或为什么新代码修复它。
    • @EricPostpischil 这确实说明了“您没有覆盖第一个字符串 null”的关键问题,这至少是部分解释。
    猜你喜欢
    • 2015-09-22
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 2010-11-20
    • 2015-09-22
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多