C 缓冲区的指针与双指针

C 缓冲区的指针与双指针,c,pointers,C,Pointers,我在内存中有一个很大的ish字符串,我想用函数来提供这些片段,例如,从30开始给我一个14字符的运行。为了实现这一点,我必须让消费者使用双指针作为缓冲区 void foo(char** bar) { char* tmp = "nope"; *bar = &tmp[1]; } int main (int argc, char const *argv[]) { char* baz = "yep"; foo(&baz); printf("%s"

我在内存中有一个很大的ish字符串,我想用函数来提供这些片段,例如,从30开始给我一个14字符的运行。为了实现这一点,我必须让消费者使用双指针作为缓冲区

void foo(char** bar)
{
    char* tmp = "nope";
    *bar = &tmp[1];
}

int main (int argc, char const *argv[])
{
    char* baz = "yep";
    foo(&baz);
    printf("%s", baz);
    return 0;
}
但是,如果我查看各种库函数,如pread或strcpy,它们只接受一个指针级别的字符串缓冲区


他们是怎么做到的?似乎它们也需要一个双指针来以同样的方式更改另一端的值。

大多数这样的函数不会更改指针,而是更改内存。strcpy的原型是:

这意味着dest指向的内存将被来自源的内容覆盖,直到找到并包括第一个0字符

调用方将使用要修改的缓冲区的地址调用此函数

对于您的情况,听起来好像您正在进行零拷贝字符串切片,那么使用指针到指针就更有意义了。但是请注意,终止将成为一个问题,您不能在不需要复制的情况下对C字符串进行切片,因为否则原始字符串也将被切片

当然,您可以定义一个复制字符串切片函数:

char * slice_string(char *dest, size_t dest_max,
                    const char *source, int start_index, int end_index);
这将把字符从源复制到dest,就像strcpy一样,但只复制一个子字符串。例如,我对索引进行了签名,以便支持Python的负索引


请注意,该函数不称为strslice,因为该函数名是保留的。

大多数此类函数不会更改指针,而是更改内存。strcpy的原型是:

这意味着dest指向的内存将被来自源的内容覆盖,直到找到并包括第一个0字符

调用方将使用要修改的缓冲区的地址调用此函数

对于您的情况,听起来好像您正在进行零拷贝字符串切片,那么使用指针到指针就更有意义了。但是请注意,终止将成为一个问题,您不能在不需要复制的情况下对C字符串进行切片,因为否则原始字符串也将被切片

当然,您可以定义一个复制字符串切片函数:

char * slice_string(char *dest, size_t dest_max,
                    const char *source, int start_index, int end_index);
这将把字符从源复制到dest,就像strcpy一样,但只复制一个子字符串。例如,我对索引进行了签名,以便支持Python的负索引

请注意,函数名不是strslice,因为该函数名是保留的。

和 不要改变指针指向的位置;它们更改缓冲区包含的内容。因此,它们不需要双指针表示法

您可能会在以下方面做得最好:

const char *foo(void);
这样可以避免使用双指针,并允许您编写:

char *baz = foo();
在您的示例主程序中

表示无空终止的字符串片段需要注意起始位置和长度。也许您将使用带有指针和长度的结构,在这种情况下,您可以将指向此类结构的指针传递给函数,或者返回该结构的副本。

和 不要改变指针指向的位置;它们更改缓冲区包含的内容。因此,它们不需要双指针表示法

您可能会在以下方面做得最好:

const char *foo(void);
这样可以避免使用双指针,并允许您编写:

char *baz = foo();
在您的示例主程序中


表示无空终止的字符串片段需要注意起始位置和长度。也许您将使用带有指针和长度的结构,在这种情况下,您可以将指向此类结构的指针传递给函数,或者返回该结构的副本。

您需要区分指针和指针对象。考虑一个函数,它调用一个这样的单级指针:

char *p = ...;
f(p);
char *p;
g(&p);
f收到p的副本。这个拷贝指向与p相同的内存,因此f可以更改p指向的内存中的值,但不能更改p指向的内存块。在契约中,接受双指针的函数调用如下:

char *p = ...;
f(p);
char *p;
g(&p);

g将能够做f所能做的一切,使用*给出p值的_参数。但除此之外,它可以改变p,所以它可以使p指向不同的内存块。这对于大多数函数都不是必需的,但有时很有用。

您需要区分指针和指针对象。考虑一个函数,它调用一个这样的单级指针:

char *p = ...;
f(p);
char *p;
g(&p);
f收到p的副本。这个拷贝指向与p相同的内存,因此f可以更改p指向的内存中的值,但不能更改p指向的内存块。在契约中,接受双指针的函数调用如下:

char *p = ...;
f(p);
char *p;
g(&p);
g将能够做f所能做的一切,使用*给出p值的_参数。但除此之外,它可以改变p,所以它可以使p指向不同的内存块。这是不必要的
对于大多数函数来说,这是一个双指针,但有时也很有用。

似乎它们也需要一个双指针来更改值-否。一个级别的间接寻址就足够了,它们不会更改指针,只更改指针指向的值。当您将*bar设置为&tmp[1]时,您将它指向一个基于堆栈的自动变量,当foo返回时,该变量将超出范围。您想修复它吗?这是示例代码。在实际代码中,这是堆上的一个数兆字节二进制blob。看起来他们还需要一个双指针来更改值-nope。一个级别的间接寻址就足够了,它们不会更改指针,只更改指针指向的值。当您将*bar设置为&tmp[1]时,您将它指向一个基于堆栈的自动变量,当foo返回时,该变量将超出范围。您想修复它吗?这是示例代码。在实际代码中,它是堆上的一个数兆字节二进制blob。