Tại sao không memset được với giá trị khởi tạo bằng 1?

Hi,

Mình mới làm quen với C, đang tìm hiểu hàm memset() thì không hiểu tại sao khởi tạo giá trị cho mảng số nguyên int là 0 hoặc -1 thì được, còn 1 thì không được.

memset

Mình search google thì có kết quả như giải thích là:

memset can’t be used to initialize int array with 1 because if int is represented by 4 bytes, then it will initialize each bytes with 1 .
16843009 is equivalent to 0x01010101 . Each of 4 bytes are initialized with 01 .
Using memset , an array of int can only be initialised with 0 or -1 because 0 and -1 both have all bits 0 and 1 respectively in two’s complement binary representation regardless of the size of int data type.

Mình hiểu như bên dưới có đúng không?
・nếu khởi tạo bằng 1 thì 4 bytes sau khi khởi tạo sẽ là 01 01 01 01 → không được
・nếu khởi tạo bằng 0 thì 4 bytes sau khi khởi tạo sẽ là 0 0 0 0 → OK
・nếu khởi tạo bằng -1 thì 4 bytes sau khi khởi tạo sẽ là 1 1 1 1 → OK

Không phải, nó sẽ thành 0xFF FF FF FF = -1.

6 Likes

Cảm ơn bạn đã feedback

Mình tìm hiểu thì:
Theo trang https://www.cplusplus.com/reference/cstring/memset/
void * memset ( void * ptr, int value, size_t num );
value là ký tự unsigned char

Mình chưa hiểu lắm, nếu là FF sao thành 1 byte dc nhỉ

printf("%d", sizeof('FF')); // 4 bytes

Có phải là 0xF F F F !?

printf("%d", sizeof('F')); // 1 byte

https://www.rapidtables.com/convert/number/decimal-to-hex.html

Cùng với giải thích ở trên:

16843009 is equivalent to 0x01010101 . Each of 4 bytes are initialized with 01

Mỗi byte sẽ được khởi tạo là 01 = 2 ký tự unsigned char
0x01010101 = 2 x 4 = 8 bytes vượt quá kích thước 4 bytes của kiểu int
=> Do đó xảy ra việc khởi tạo cho hàm memset() giá trị bằng 1 đã có kết quả không đúng như mong đợi

Mình đang hiểu nguyên nhân là như vậy không biết có đúng không!!?

1 byte = 8 bit

  • hệ cơ số 2 sẽ được biểu diễn bằng 8 kí tự 0,1
  • hệ cơ số 16 sẽ được biểu diễn bằng 2 ký tự 0,1,2,3…,8,9,a,b,c,d,e,f. 4 bit cơ số 2 = 1 ký tự 0-f ở cơ số 16.
  • hệ cơ số 10 sẽ được biểu diễn bằng (tối đa) 3 ký tự 0-9.

0x01 = 0b00000001 = 1*16^0 + 0*16^1 = 1
0xff = 0b11111111 = 15*16^0 + 15*16^1 = 255


với số nguyên có dấu thì 0xFF sẽ được hiểu theo nghĩa khác. Để tiện cho phép cộng khi 1 số nguyên 8 bit x cộng với 1 số nguyên 8 bit y nếu x = -y thì x + y = 0, máy tính sẽ cho giá trị của số nguyên có dấu theo quy tắc bù 2: nếu bit cao nhất của x là 1 thì giá trị của x là trừ của (x đảo bit) + 1:

0xFF = 0b11111111

đảo bit 1 thành 0, 0 thành 1:
0b00000000

cộng 1:
0b00000001 = 1

vậy giá trị của số nguyên có dấu 0xFF là -1


tính tiện lợi của quy tắc bù 2: lấy 0xFF + 0x01 = 0
  0b11111111
+ 0b00000001
=0b100000000 số 1 bị bỏ đi vì nó vượt quá 8 bit, còn lại 0b00000000 = 0

số nguyên có dấu int 32 bit = 4 byte = biểu diễn với 8 ký tự trong hệ cơ số 16, 32 ký tự trong hệ cơ số 2 (tối đa 10 ký tự trong hệ cơ số 10)
0x01010101 = 0b00000001.00000001.00000001.00000001 (viết thêm dấu . cho dễ nhìn), bit cao nhất (bên trái) = 0, tính giá trị bình thường: 1*16^0 + 0*16^1 + 1*16^2 + 0*16^3 + 1*16^4 + 0*16^5 + 1*16^6 + 0*16^7 = 1 + 256 + 65536 + 16777216 = 16843009
0xFFFFFFFF = 0b11111111.11111111.11111111.11111111, bit cao nhất (bên trái) = 1, áp dụng quy tắc bù 2 tính giá trị là -1


trong trang cplusplus kia có ghi rõ ràng mà ko đọc kĩ à :V

The value is passed as an int, but the function fills the block of memory using the unsigned char conversion of this value .

dù được pass là int nhưng memset sẽ chuyển giá trị này thành unsigned char, ở đây là unsigned int 8 bit. 0x00000001 (1) sẽ được convert thành 0x01 (1). 0xffffffff (-1) sẽ được convert thành 0xff (255 vì ko dấu).

  • memset copy vào 20 bytes kia giá trị 0xff thì 20 bytes đó đều có giá trị là 0xff:

    [0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff][0xff]
    hay ở dạng binary
    [11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111][11111111]
    

    sau đó printf ra thì a[i] lại được hiểu là int có dấu 4 byte:

    [11111111.11111111.11111111.11111111][11111111.11111111.11111111.11111111][11111111.11111111.11111111.11111111][11111111.11111111.11111111.11111111][11111111.11111111.11111111.11111111]
    hay ở hệ cơ số 16
    [0xffffffff][0xffffffff][0xffffffff][0xffffffff][0xffffffff]
    

    đều có giá trị -1 hết

  • memset copy vào 20 bytes kia giá trị 0x01 thì 20 bytes đó đều có giá trị là 0x01:

    [0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01][0x01]
    hay ở dạng binary
    [00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001][00000001]
    

    sau đó printf ra thì a[i] lại được hiểu là int có dấu 4 byte:

    [00000001.00000001.00000001.00000001][00000001.00000001.00000001.00000001][00000001.00000001.00000001.00000001][00000001.00000001.00000001.00000001][00000001.00000001.00000001.00000001]
    hay ở hệ cơ số 16
    [0x01010101][0x01010101][0x01010101][0x01010101][0x01010101]
    

    đều có giá trị là 16843009


nếu thử gán với -2 = 0xfffffffe thì memset sẽ convert nó thành 0xfe (254) rồi gán vào từng byte. a[i] sẽ có giá trị 0xfefefefe = -16843010 (vì 0xfefefefe + 0x01010101 = 0xffffffff = -1 nên suy ra 0xfefefefe = -1 - 0x01010101 :V Nếu tính bù 2 cũng được: đảo bit 0xfefefefe = 0x01010101 = 16843009, cộng 1 = 16843010, vậy kết quả là -16843010)


còn có vụ endianess little/big endian nữa nhưng vì ở đây 4 byte của int 32 bit đều giống nhau nên ko quan trọng :V

7 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?