在C语言中使用memset设置整数数组

54
char str[] = "beautiful earth";
memset(str, '*', 6);
printf("%s", str);

Output:
******ful earth

像上面使用memset一样,我们能否只将几个整数数组索引值初始化为1,如下所示?

int arr[15];
memset(arr, 1, 6);

3
可能是memset整型数组?的重复问题。 - Joe Frambach
4
不。 memset() 将其转换为一个字节并在整个区域内进行复制。 - Mysticial
1
你可以阅读memset文档以了解它的作用。 - Jim Balter
1
@JoeFrambach 那个问题的被接受答案在这里不适用。 - Jim Balter
可能是[为什么“memset(arr,-1,sizeof(arr)/ sizeof(int))”不能将整数数组清除为-1?]的重复问题。(链接:https://dev59.com/qmw05IYBdhLWcg3wfx80) - Andrik007
10个回答

88

不,你不能像这样使用memset()函数。该手册页中指出(重点是我的):

memset()函数将由s指向的内存区域的前n字节用常数c填充。

由于一个int通常是4个字节,因此这种用法行不通。


如果您(错误地!)尝试这样做:

int arr[15];
memset(arr, 1, 6*sizeof(int));    //wrong!

如果数组的前6个int实际上被设置为0x01010101 = 16843009,则可以使用该语句。

唯一真正可接受的用非字节数据类型覆盖“blob”数据的情况是使用memset(thing, 0, sizeof(thing));函数来将整个结构/数组“清零”。这能够奏效是因为NULL、0x00000000、0.0都是完全的零。


解决方法是使用一个for循环并手动进行设置:

int arr[15];
int i;

for (i=0; i<6; ++i)    // Set the first 6 elements in the array
    arr[i] = 1;        // to the value 1.

1
如果你先用“int arr[15] = {0}”将整个数组初始化为0,然后再使用“memset(arr, 1, 8)”进行操作,我会更相信你的“memset”是这么做的。如果在此之后你仍然得到了一个充满“0x01010101”的数组,那么你的 c 运行库可能有问题 :-)。 - Mike Woolf
1
@MikeWoolf 对的。我的评论是针对OP的。当然,他可以自由编写任何他想要的代码,但如果他希望以某种方式“欺骗”memset来初始化他的数组,那结果将会很糟糕。 - Jonathon Reinhart
2
@briankip 这里的数组会衰变为指针。在这种情况下,&是可选的;memset(&arr, ...)将执行相同的操作。 - Jonathon Reinhart
1
@JonathonReinhart 这里的措辞有些危险(“*'&是可选的'*”)。两种变体都可以工作,因为memset接受void*,但指针类型是不同的:arr衰减为int*,而&arr给出了一个类型为int(*)[15]的指针! - Aconcagua
1
@Jacqueline P. 如果您认为这个答案不正确,正确的做法是留下评论,而不是建议编辑。这样我就可以解释为什么您的理解不完全正确。 - Jonathon Reinhart
显示剩余7条评论

26

简短回答,不行。

详细回答,memset用于设置字节,可以对字符起作用,因为它们是单个字节,但整数不是。


7
在Linux、OSX和其他类UNIX操作系统中,其中wchar_t是32位的,您可以使用wmemset()而不是memset()
#include<wchar.h>
...
int arr[15];
wmemset( arr, 1, 6 );

请注意,在MS-Windows上,wchar_t是16位的,因此这个技巧可能行不通。

1
为什么要使用这种不可移植的hack,而不是一个简单的循环? - Ayxan Haqverdili

5

memset的第三个参数是字节数。因此,您应该设置arr[15]的总字节数。

memset(arr, 1, sizeof(arr));

但是,也许你希望将值1设置给arr中的所有元素。那么最好在循环中设置。

for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) {
    arr[i] = 1;
}

因为memset()会将每个字节设为1,所以这不符合您的预期。

但在我的系统中,int的大小为4个字节。因此,我尝试使用memset(arr,1,8)。但它将整个数组都用这个4字节的值进行初始化,而不是根据我的第三个参数仅填充前8个字节。 - user1762571
你不应该使用常量值8,最好使用元素大小的计数。例如 n * sizeof(int) - mattn
2
当调用memset(arr, 1, 4)时,它变成了0x01 0x01 0x01 0x01。但你期望的是0x00 0x00 0x00 0x01。(这取决于你的CPU架构)。 - mattn
明白了,谢谢。我按照你说的尝试使用memset(arr,1,2*sizeof(int));,但仍然将整个数组初始化为相同的值。 - user1762571
正如我在上面所写的,如果您想将每个元素设置为1(而不是每个字节设置为1),则不应使用memset。 - mattn
不,它只设置了8个字节。你可能在某种程度上误解了结果,或者将初始字节复制到数组的其余部分。@user1762571“它正在使用那个4字节值初始化整个数组,而不是根据我的第三个参数仅填充前8个字节。” - Jim Balter

4

既然没有人提到它...

虽然你不能使用memset来初始化整数为值1,但是你可以使用值-1来初始化它们,并简单地改变你的逻辑以使用负值。

例如,要使用-1初始化数组的前6个数字,你可以这样做:

memset(arr,-1,6*(sizeof int));

此外,如果您只需要进行一次初始化操作,您实际上可以在编译时声明包含值1的数组。

int arr[15] = {1,1,1,1,1,1};

4
你在技术上是正确的,但如果我在代码审查中看到这样的内容,我可能会崩溃。 - Jonathon Reinhart
3
这只适用于使用二进制补码表示负数的平台。尽管今天几乎每个平台都是二进制补码,但这仍然是一种技巧。 - AnT stands with Russia

3
实际上,使用memset_pattern4是可以实现的,它可以每次设置4个字节。
memset_pattern4(your_array, your_number, sizeof(your_array));

1
它每次设置4个字节,而不是4个位。 - phuclv

2
不,你不能 [可移植地] 使用 memset 来实现此目的,除非所需的目标值是0memset 将目标内存区域视为字节数组而非int 数组。

一个相当流行的内存区域填充hack基于memcpy。它关键依赖于memcpy按正向方向复制数据的期望。

int arr[15];

arr[0] = 1;
memcpy(&arr[1], &arr[0], sizeof arr - sizeof *arr);

这当然是一种相当丑陋的方法,因为标准的 memcpy 在源内存区域和目标内存区域重叠时其行为是未定义的。不过你可以自己编写 memcpy 的版本,确保它向前复制数据,并以上述方式使用。但这并不值得。只需使用简单循环将数组元素设置为所需值即可。

memcpy在一次迭代中复制多个字节以进行优化,因此我认为这种技巧至少无法用于大于8个字符的字符串。 - Fayeure

0
以下程序展示了我们可以使用memset()函数仅用-1和0来初始化数组。
#include<stdio.h>
#include<string.h>

void printArray(int arr[], int len)
{
        int i=0;
    for(i=0; i<len; i++)
    {
        printf("%d ", arr[i]);
    }
    puts("");
}

int main()
{
    int arrLen = 15;
    int totalNoOfElementsToBeInitialized = 6;

    int arr[arrLen];
    printArray(arr, arrLen);
    memset(arr, -1, totalNoOfElementsToBeInitialized*sizeof(arr[0]));
    printArray(arr, arrLen);
    memset(arr, 0, totalNoOfElementsToBeInitialized*sizeof(arr[0]));
    printArray(arr, arrLen);
    memset(arr, 1, totalNoOfElementsToBeInitialized*sizeof(arr[0]));
    printArray(arr, arrLen);
    memset(arr, 2, totalNoOfElementsToBeInitialized*sizeof(arr[0]));
    printArray(arr, arrLen);
    memset(arr, -2, totalNoOfElementsToBeInitialized*sizeof(arr[0]));
    printArray(arr, arrLen);
    return 0;
}

0
最好不要使用memset将数组全部设置为1。
因为memset是按字节操作的,会将每个字节都设置为1。
memset(hash, 1, cnt);

读取后,它将显示的值是16843009 = 0x01010101 = 1000000010000000100000001
而不是0x00000001
但如果你只需要 bool 或二进制值,则可以使用 C99 标准来设置 C 库。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>        //Use C99 standard for C language which supports bool variables

int main()
{
    int i, cnt = 5;
    bool *hash = NULL;
    hash = malloc(cnt);

    memset(hash, 1, cnt);
    printf("Hello, World!\n");

    for(i=0; i<cnt; i++)
        printf("%d ", hash[i]);

    return 0;
}

输出:

你好,世界!
1 1 1 1 1


0

Memset函数用于设置1字节的数据类型的值,但整数占用4个或更多字节,因此它不起作用,你会得到垃圾值。 它主要用于处理char和string类型的数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接