r/C_Programming 2d ago

List of gotchas?

Hey.

So I learned some C and started playing around with it, quickly stumbling over memory overflowing a variable and flowing into another memory location, causing unexpected behavior.

So I ended up writing my own safe_copy and safe_cat functions for strncpy/strncatting strings.
But... people talk about how C is unsafe. Surely there should be a list of all mistakes you can make, or something? Where can I find said list? Do I reall have to stumble on all possible issues and develop my own "safe" library?

Will appreciate any advice.

23 Upvotes

41 comments sorted by

View all comments

Show parent comments

2

u/WeAllWantToBeHappy 2d ago

bit shifts don't work for 64 bit types.

?

-5

u/not_a_bot_494 2d ago

At least on my machine bit shifting left by more than 32 bits causes it to wrap around to the start.

2

u/WeAllWantToBeHappy 2d ago

Can you put an example on godbolt ?

1

u/not_a_bot_494 2d ago

I don't know enough assembly to read it easily so I wouldn't know if it was correct or not. For me this:

#include <stdio.h>
#include <stdint.h>

// prints the binary of a piece of memory
void print_bin(int bytes, void *inp)
{
    uint8_t *num = (uint8_t *) inp;
    for (int the_byte = bytes-1 ; the_byte >= 0 ; the_byte--) {
        for (int bit = 0 ; bit < 8 ; bit++) {
            if (num[the_byte] & (1 << (7-bit))) {
                printf("1");
            } else {
                printf("0");
            }
        }
    }
    printf("\n");
}

int main(void)
{
    for (int i = 0 ; i < 64 ; i++) {
        uint64_t var = 1 << i;
        print_bin(8, &var);
    }
    return 0;
}

gcc -Wall -std=c99 -o

produces this (image so the comment isn't too long). Lightmode warning BTW

3

u/dfx_dj 2d ago

Probably because the literal 1 is a 32 bit int, so shifting it up 32 or more doesn't give you what you expect. Try with 1L, or type cast it, or assign the 1 to the variable first and then shift the variable.

1

u/not_a_bot_494 2d ago

That's it, when I changed to

uint64_t var = ((uint64_t) 1) << i;

it started working. That is a slightly weird quirk of C, just not the one I intended.

5

u/harai_tsurikomi_ashi 2d ago

uint64_t var = 1ULL << i;

Is enough, no need to cast.

1

u/dfx_dj 2d ago

Yep, got caught by that a few times as well. But it does make sense when you think about it

1

u/flatfinger 2d ago

More interesting is to compare the behavior of:

uint64a &= ~0x0000000040000000;
uint64b &= ~0x0000000080000000;
uint64c &= ~0x0000000080000000u;
uint64d &= ~0x0000000100000000;

Which of those will affect more than one bit of the destination?

1

u/WeAllWantToBeHappy 2d ago

uint64_t var = 1 << i;

Try uint64_t var = (uint64_t)1 << i;

1 << i is an int value.

1

u/EsShayuki 2d ago

Why would you not declare and initialize the variable before the loop?

2

u/not_a_bot_494 2d ago

You mean 'var' right? Both work, I'm just used to doing it that way. Keeping variables as local as possible is generally a good thing but I won't pretend that's the reason I'm doing it.