Printing out values of a struct byte by byte - c

Basically, I'm trying to cast a char array to a pointer to a struct and print out the values in the struct byte by byte. This is because some legacy code I'm dealing with uses a macro to do some bitwise shifting and masking to change 4 bits within an integer in a struct, and I'm trying to find out whether the macro is working as intended. However, I've compressed my problem to this toy example-
#include <stdio.h>
#include <stdint.h>
int main()
{
struct student
{
int32_t hi;
int8_t test
};
struct student p1 = {20, 20};
for(int i = 0; i < sizeof(p1); i++)
{
printf("%c",((char*)&p1)[i]);
}
return 0;
}
For some reason, this prints out two characters (that show up as boxes with 0014 in them) and nothing else. If I change the for loop to go from 0 to 5 (which is roughly the bounds of the for loop that I'm expecting) I get the exact same output. Is there a way to do what I want (i.e. get this to print out 5 characters)? I know that some of the characters will just be empty spaces, but I don't understand how I'm not getting said empty spaces.

You're trying to print binary values as characters. How those characters get printed depends on your console, but byte values of 0 typically don't print anything.
If you want to see what the values of the bytes are, print them as hex numbers instead:
printf("%02x ",((char*)&p1)[i]);

Read more about ASCII. Some characters are control characters so are not shown nicely, and your system might have some weird character encoding. Be aware than in 2019, UTF-8 is used everywhere (almost).
You'll better print your bytes in hexadecimal:
printf("%02x",(unsigned int) ((char*)&p1)[i]);
and you might have an implementation with signed char -s, so it could be better to replace char* with an explicit unsigned char* (or even better uint8_t* from <stdint.h>). Notice that it is documented that printf with %02x wants an unsigned integer (not simply a char). Anything else could be undefined behavior.
Refer at least to some C reference site, and consider looking into the C11 standard n1570.
Of course, enable all warnings and debug info in your compiler (with GCC, compile with gcc -Wall -Wextra -g) and use the debugger (e.g. use gdb). So read How to debug small programs.

Related

Why is my output wrong? C newbie

#include <stdio.h>
int main(void)
{
char username;
username = '10A';
printf("%c\n", username);
return 0;
}
I just started learning C, and here is my first problem. Why is this program giving me 2 warnings (multi-character constant, overflow in implicit constant conversion)?
And instead of giving 10A as output, it is giving just A.
You are trying to stuff multiple characters into a single set of '', and into a single char variable. You need "" for string literals, and you'll need an array of characters to hold a string. And to print a string, use %s.
Putting all of this together, you get:
#include <stdio.h>
int main(void)
{
char username[] = "10A";
printf("%s\n", username);
return 0;
}
Footnote
From Jonathan Leffler in the comments below regarding multi-character constants:
Note that multi-character constants are a part of C (hence the warning, not an error), but the value of a multi-character constant is implementation defined and hence not portable. It is an integer value; it is larger than fits in a char, so you get that warning. You could have gotten almost anything as the output — 1, A and a null byte could all be plausible.
'10A' is an allowed but obscure way to define a value.
In the case of an int variable,
int username = '10A';
printf("%x\n", username);
will output
313041
These are pairs of hexadecimal values - each pair is
0x31 is the '1' of your input.
0x30 is the '0' of your input.
0x41 is the 'A' of your input.
But a char type can't hold this.
In C there are no String objects. Instead Strings are arrays of characters (followed by a null character). Other answers have pointed out statically allocating this memory. However I recommend dynamically allocating Strings. Just remember C lacks a garbage memory collector (like there is in java). So remember to free your pointers. Have fun!!
You could use char *username to point to the beginning of the address and loop through the memory after. For instance use sizeof(username) to get the size and then loop printf until you have printed the amount of characters in username. However you may end up with major problems if you aren't careful...

printing the hex values after storing it to an array using C

I have done the reading from a file and is stored as hex values(say the first value be D4 C3). This is then stored to a buffer of char datatype. But whenever i print the buffer i am getting a value likebuff[0]=ffffffD4; buff[1]=ffffffC3 and so on.
How can I store the actual value to the buffer without any added bytes?
Attaching the snippet along with this
ic= (char *)malloc(1);
temp = ic;
int i=0;
char buff[1000];
while ((c = fgetc(pFile)) != EOF)
{
printf("%x",c);
ic++;
buff[i]=c;
i++;
}
printf("\n\nNo. of bytes written: %d", i);
ic = temp;
int k;
printf("\nBuffer value is : ");
for(k=0;k<i;k++)
{
printf("%x",buff[k]);
}
The problem is a combination of two things:
First is that when you pass a smaller type to a variable argument function like printf it's converted to an int, which might include sign extension.
The second is that the format "%x" you are using expects the corresponding argument to be an unsigned int and treat it as such
If you want to print a hexadecimal character, then use the prefix hh, as in "%hhx".
See e.g. this printf (and family) reference for more information.
Finally, if you only want to treat the data you read as binary data, then you should consider using int8_t (or possibly uint8_t) for the buffer. On any platform with 8-bit char they are the same, but gives more information to the reader of the code (saying "this is binary data and not a string").
By default, char is signed on many platforms (standards doesn't dictate its signedness). When passing to variable argument list, standard expansions like char -> int are invoked. If char is unsigned, 0xd3 remains integer 0xd3. If char is signed, 0xd3 becomes 0xffffffd3 (for 32-bit integer) because this is the same integer value -45.
NB if you weren't aware of this, you should recheck the entire program, because such errors are very subtle. I've dealed once with a tool which properly worked only with forced -funsigned-char into make's CFLAGS. OTOH this flag, if available to you, could be a quick-and-dirty solution to this issue (but I suggest avoiding it for any longer appoaching).
The approach I'm constantly using is passing to printf()-like functions a value not c, but 0xff & c, it's visually easy to understand and stable for multiple versions. You can consider using hh modifier (UPD: as #JoachimPileborg have already suggested) but I'm unsure it's supported in all real C flavors, including MS and embedded ones. (MSDN doesn't list it at all.)
You did store the actual values in the buffer without the added bytes. You're just outputting the signed numbers with more digits. It's like you have "-1" in your buffer but you're outputting it as "-01". The value is the same, it's just you're choosing to sign extend it in the output code.

Preceding zeros are ignored

I am modifying airodump-ng to build a custom application.
I need the output in this format
{AP Mac 1, Station Mac 1},{AP Mac 2, Station Mac 2},...............
To do this I traverse through struct ST_INFO and using multiple strcat calls I generate an array in the above format.
The problem arises when the MAC address contains preceding zeros and this results in data corruption
eg: 0A1B23443311 is saved as A1B23443311
eg: 001B3311ff22 is saved as 1B3311ff22 ( The 0s have been ignored)
What should I do so that data is saved properly when MAC address contains preceding zeros?
The final array is written to a file.
Update: Printing leading 0's in C?
When I tried to print the MAC address the results were the same as given in the above examples but when I used %02x (I learned about it from above link) the problem was solved when I want to print.
Since, I want to save the contents to an array, is there any trick like the %02x for printf.
The struct ST_INFO contains unsigned char st_mac[6] (MAC address is stored in hex format) and my final array is also unsigned char array.
There are multiple ways to do, but if you're using snprintf() or one of its relatives, the %02x (or maybe %02X, or %.2x or %.2X) formats will be useful. For example:
const unsigned char *st_mac = st_info_struct.st_mac;
unsigned char buffer[13];
for (int i = 0; i < 6; i++)
sprintf(&buffer[2*i], "%.2X", st_mac[i]);
(Usually, using snprintf() is a good idea; here, it is unnecessary overkill, though it would not be wrong.)
You should not be using multiple strcat() calls to build up the string. That leads to a quadratic algorithm. If the strings are long (say kilobytes or more), this begins to matter. You also shouldn't be using strcat() because you need to know how long everything is (the string you've created so far, and the string you're adding to it) so that you can ensure you don't overflow your storage space.
if there is a fixed length for all the addresses, just check the length before appending it. If the
length < fixed_length , append difference between the length's number of zeroes.

Seg Fault with isdigit() in C?

I have this code:
#include <ctype.h>
char *tokenHolder[2500];
for(i = 0; tokenHolder[i] != NULL; ++i){
if(isdigit(tokenHolder[i])){ printf("worked"); }
Where tokenHolder holds the input of char tokens from user input which have been tokenized through getline and strtok. I get a seg fault when trying to use isdigit on tokenHolder — and I'm not sure why.
Since tokenHolder is an array of char *, when you index tokenHolder[i], you are passing a char * to isdigit(), and isdigit() does not accept pointers.
You are probably missing a second loop, or you need:
if (isdigit(tokenHolder[i][0]))
printf("working\n");
Don't forget the newline.
Your test in the loop is odd too; you normally spell 'null pointer' as 0 or NULL and not as '\0'; that just misleads people.
Also, you need to pay attention to the compiler warnings you are getting! Don't post code that compiles with warnings, or (at the least) specify what the warnings are so people can see what the compiler is telling you. You should be aiming for zero warnings with the compiler set to fussy.
If you are trying to test that the values in the token array are all numbers, then you need a test_integer() function that tries to convert the string to a number and lets you know if the conversion does not use all the data in the string (or you might allow leading and trailing blanks). Your problem specification isn't clear on exactly what you are trying to do with the string tokens that you've found with strtok() etc.
As to why you are getting the core dump:
The code for the isdigit() macro is often roughly
#define isdigit(x) (_Ctype[(x)+1]&_DIGIT)
When you provide a pointer, it is treated as a very large (positive or possibly negative) offset to an array of (usually) 257 values, and because you're accessing memory out of bounds, you get a segmentation fault. The +1 allows EOF to be passed to isdigit() when EOF is -1, which is the usual value but is not mandatory. The macros/functions like isdigit() take either an character as an unsigned char — usually in the range 0..255, therefore — or EOF as the valid inputs.
You're declaring an array of pointer to char, not a simple array of just char. You also need to initialise the array or assign it some value later. If you read the value of a member of the array that has not been initialised or assigned to, you are invoking undefined behaviour.
char tokenHolder[2500] = {0};
for(int i = 0; tokenHolder[i] != '\0'; ++i){
if(isdigit(tokenHolder[i])){ printf("worked"); }
On a side note, you are probably overlooking compiler warnings telling you that your code might not be correct. isdigit expects an int, and a char * is not compatible with int, so your compiler should have generated a warning for that.
You need/want to cast your input to unsigned char before passing it to isdigit.
if(isdigit((unsigned char)tokenHolder[i])){ printf("worked"); }
In most typical encoding schemes, characters outside the USASCII range (e.g., any letters with umlauts, accents, graves, etc.) will show up as negative numbers in the typical case that char is a signed.
As to how this causes a segment fault: isdigit (along with islower, isupper, etc.) is often implemented using a table of bit-fields, and when you call the function the value you pass is used as an index into the table. A negative number ends up trying to index (well) outside the table.
Though I didn't initially notice it, you also have a problem because tokenHolder (probably) isn't the type you expected/planned to use. From the looks of the rest of the code, you really want to define it as:
char tokenHolder[2500];

New to C: whats wrong with my program?

I know my way around ruby pretty well and am teaching myself C starting with a few toy programs. This one is just to calculate the average of a string of numbers I enter as an argument.
#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
char *token;
int sum = 0;
int count = 0;
token = strtok(argv[1],",");
while (token != NULL)
{
count++;
sum += (int)*token;
token = strtok(NULL, ",");
}
printf("Avg: %d", sum/count);
printf("\n");
return 0;
}
The output is:
mike#sleepycat:~/projects/cee$ ./avg 1,1
Avg: 49
Which clearly needs some adjustment.
Any improvements and an explanation would be appreciated.
Look for sscanf or atoi as functions to convert from a string (array of characters) to an integer.
Unlike higher-level languages, C doesn't automatically convert between string and integral/real data types.
49 is the ASCII value of '1' char.
It should be helpful to you....:D
The problem is the character "1" is 49. You have to convert the character value to an integer and then average.
In C if you cast a char to an int you just get the ASCII value of it. So, you're averaging the ascii value of the character 1 twice, and getting what you'd expect.
You probably want to use atoi().
EDIT: Note that this is generally true of all typecasts in C. C doesn't reinterpret values for you, it trusts you to know what exists at a given location.
strtok(
Please, please do not use this. Even its own documentation says never to use it. I don't know how you, as a Ruby programmer, found out about its existence, but please forget about it.
(int)*token
This is not even close to doing what you want. There are two fundamental problems:
1) A char* does not "contain" text. It points at text. token is of type char*; therefore *token is of type char. That is, a single byte, not a string. Note that I said "byte", not "character", because the name char is actually wrong - an understandable oversight on the part of the language designers, because Unicode did not exist back then. Please understand that char is fundamentally a numeric type. There is no real text type in C! Interpreting a sequence of char values as text is just a convention.
2) Casting in C does not perform any kind of magical conversions.
What your code does is to grab the byte that token points at (after the strtok() call), and cast that numeric value to int. The byte that is rendered with the symbol 1 actually has a value of 49. Again, interpreting a sequence of bytes as text is just a convention, and thus interpreting a byte as a character is just a convention - specifically, here we are using the convention known as ASCII. When you hit the 1 key on your keyboard, and later hit enter to run the program, the chain of events set in motion by the command window actually passed a byte with the value 49 to your program. (In the same way, the comma has a value of 44.)
Both of the above problems are solved by using the proper tools to parse the input. Look up sscanf(). However, you don't even want to pass the input to your program this way, because you can't put any spaces in the input - each "word" on the command line will be passed as a separate entry in the argv[] array.
What you should do, in fact, is take advantage of that, by just expecting each entry in argv[] to represent one number. You can again use sscanf() to parse each entry, and it will be much easier.
Finally:
printf("Avg: %d", sum/count)
The quotient sum/count will not give you a decimal result. Dividing an integer by another integer yields an integer in C, discarding the remainder.
In this line: sum += (int)*token;
Casting a char to an int takes the ASCII value of the char. for 1, this value is 49.
Use the atoi function instead:
sum += atoi(token);
Note atoi is found in the stdlib.h file, so you'll need to #include it as well.
You can't convert a string to an integer via
sum += (int)*token;
Instead you have to call a function like atoi():
sum += atoi (token);
when you cast a char (which is what *token is) to int you get its ascii value in C - which is 49... so the average of the chars ascii values is in fact 49. you need to use atoi to get the value of the number represented

Resources