For a bit of background, I'm writing a meter reading application in C for a small 16-bit handheld computer that runs a proprietary version of DOS.
I have a screen that displays meter information and prompts the user to type in a reading. When the user presses the enter key on the unit, the following code will execute:
/* ...
* beginning of switch block to check for keystrokes
* ...
*/
case KEY_ENTER: {
/* show what has been entered */
if(needNew == 0) {
/* calculate usage for new reading */
double usg = 0;
int ret = CalculateNewUsage(vlr, buf, &usg);
VerifyReadScreen(vlr, ret, buf, &usg);
needRedraw = TRUE;
}
break;
}
/* .... end switch statement */
vlr is a pointer to a struct that holds all account/meter information, buf is of type char[21] used to store numerical keystrokes for the reading which is handled above this block. My variables all contain valid data when I check them both before and after calling CalculateNewUsage.
However when I check variable data again after entering VerifyReadScreen, newread is pointing somewhere random in memory and returns what looks like a copyright notice. The interesting thing is no matter what account or what reading I enter - the same invalid data for newread in VerifyReadScreen is printed on the screen. I am passing the address to VerifyReadScreen in the same manner as CalculateNewUsage, but somehow I've ended up with something different.
Here is VerifyReadScreen:
BYTE VerifyReadScreen(const VLRREC * vlr,
const int status,
const char * newread,
const double * usage) {
/* snip a whole bunch of irrelevant formatting code */
printf("%s", (*newread)); /* prints funky copyright text */
/* snip more irrelevant formatting code */
return TRUE;
}
Thank you to Jefromi for pointing out that the code where I am actually printing newread in VerifyReadScreen should really read:
printf("%s", newread); /* yay! */
because I didn't need to dereference newread because printf does this for me. I was essentially passing a pointer to a pointer which was some arbitrary place in memory.
I think I'm confident enough to post this as an answer:
BYTE VerifyReadScreen(const VLRREC * vlr, const int status, const char * newread, const double * usage) {
...
LCD_set_cursor_pos(19 - strlen(newread), 3);
printf("%s", (*newread)); /* prints funky copyright text */
...
}
You've got a string (char*) newread, but in that printf, you're dereferencing it, which gives you the first character of the string. You then use it as the argument to a %s for printf, so it tries to go to the memory address given by that character and print what it finds there.
P.S. You got unlucky - generally, doing something like this is likely to give you a segfault, so you can track it down to that line and realize there's a pointer error right there.
I don't known if that's the problem you are facing, but the buffer used in VerifyReadScreen, which is 21 characters long, will most likely overflow:
if(strlen((*vlr).ServAdd) >= 20) {
sprintf(buffer, "%20s", (*vlr).ServAdd);
}
The %20s format specifier doesn't prevent sprintf to write more than 20 characters. It just pads the string with spaces if it's shorter than 20 characters (or did you want <= 20 in the if-condition?).
else {
memset(buffer, 0x20, (int)(strlen((*vlr).ServAdd) / 2) + 1);
strcat(buffer, (*vlr).ServAdd);
}
Here some padding is done depending on the strings length, but I don't see how it would make sure that the result isn't longer than 20 characters.
Related
i'm currently trying to debug my program over UART by printing the values of some variables at specific points. My issue is with printing two char arrays. The order in which i print them seems to have an effect on if it prints. The declarations of the arrays are in a struct in a header file as follows-
//header file
typedef struct
{
char latitude[10];
char longitude[11];
}NMEA_RMC_t;
NMEA_RMC_t rmc;
The char arrays are manipulated in another function after parsing data from an input. They are not updated with an interrupt, although there are interrupts elsewhere in the program. In the main code if i print them as follows-
//main program loop
printf("lat: %s \t long: %s \n", rmc.latitude, rmc.longitude);
the output to the terminal is this-
lat: +50.71735 long:
whereas if i print them in a different order like this-
printf("long: %s \t lat: %s \n", rmc.longitude, rmc.latitude);
i get this output.
long: -001.39118 lat:
also if i split the printing up into two separate printf statements, only the first print statement correctly prints the char array. I haven't had to ask for help on here before but this has had me stuck for a good week now. Any help would be massively appreciated! cheers.
edit
The part of the program that writes into the array is this. its essentially the same for the latitude and longitude array.
/* now we are in first char of latitude */
/* copy latitude chars */
strlcpy ((char*)lat, (const char*)ptr_to_comma, 10 );/*copies most size-1, null terminated*/ //changed 11 to 10
/* default latitude presentation is ddmm.mmmmm
we need to change it to dd.mmmmmmm
*/
unsigned char ind;
for (ind=0; ind<9; ind++)
{
if (*ptr_to_comma == '.')
{
ptr_to_comma++;//step over '.'
}
if ( ind==2 )
{
lat[ind++]='.';
}
lat[ind] = *ptr_to_comma;
ptr_to_comma++;
}
lat[10] = '\0'; //terminate
/* now lat == dd.mmmmmmm */
ptr_to_comma++; /*step over comma to the NS-indicator*/
/*catch NorthSouth-indicator and step*/
if ( *ptr_to_comma == 'N'){ /*if we are in the North*/
sign = '+';
rmc.ns_indicator = 'N';
} else if ( *ptr_to_comma == 'S'){ /*else we are in the South*/
sign = '-';
rmc.ns_indicator = 'S';
}
ptr_to_comma++;//step over NS-indicator
ptr_to_comma++;//step over comma to the longitude field
/* dd.mmmmmmm to dd.ddddddd */
_convert_minutes( (unsigned char*) lat+3 );
/* copy latitude with sign to the rmc-struct */
rmc.latitude[0] = sign;
strcpy ( (char*)rmc.latitude+1, (const char*)lat);
rmc.latitude[10]='\0';
essentially it is parsing the information from a stream of data coming in.
For %s to work correctly, the char array must have a NUL-terminator \0 which signifies the end of the string.
If you don't leave room for that, the behaviour of your program is undefined.
You need to initialise the arrays yourself - don't rely on the compiler to do it.
Get your debugger out and check the memory associated with rmc at the point of the printf.
Your code for converting the strings and putting them in the struct is inadvertently placing a '\0' character at the beginning of rmc.longitude. The code then goes on the fill the rest of the array with the correct value, but the printf() function sees rmc.longitude as a zero-length string and ignores what follows.
It is an an off-by-one error. rmc.latitude[10]='\0'; places a 0 in the 11th position of the rmc.latitude array, but this array was declared to only have 10 positions. What is actually happening instead is rmc.longitude[0] is getting the '\0' character, as it is adjacent in memory.
I'm posting a continuation of my question from this thread.
I'm trying to create a string that begins with a '!' and adds 6 values read from a sensor (separated by commas) and then sends it over a serial port. A sample output would be: "!5,5,5,5,5,5" or "!34,34,34,34,34,34".
My code is mostly working; I'm able to send the value of one analog sensor across my serial port, !215!215!215 for example, but when I un-comment the for loop code below I see nothing across my serial port and the program seems, for lack of a better word, useless.
There seems to be a runtime error occuring in my for loop but I can't determine where it happens. Why does my code below successfully send serial data for one analog sensor without using the for loop, and send nothing when using the for loop? How can I tweak my code to achieve my desired output?
char* convertIntToString(uint8_t integerValue, char* str){
utoa(integerValue, str, 10);
return str;
}
char* concat(char *s1, char *s2)
{
char *result = malloc(strlen(s1)+strlen(s2)+1);
strcpy(result, s1);
strcat(result, s2);
return result;
}
int main(void){
uint8_t analogValue;
char *outputStr = malloc(1);
while (1) {
outputStr = realloc(outputStr, 2);
strcpy(outputStr, "!");
analogValue = ReadADC(0);
char str[4];
outputStr = concat(outputStr, convertIntToString(analogValue, str));
//RUNTIME ERROR IN THIS LOOP
for(int i = 0; i < 5; i++){
char* newStr = concat(outputStr, ",");
// free the old memory before using the new memory
free(outputStr);
outputStr = newStr;
newStr = concat(outputStr, convertIntToString(analogValue, str));
// free the old memory before using the new memory
free(outputStr);
outputStr = newStr;
}
CDC_Device_SendString(&VirtualSerial_CDC_Interface, outputStr); //send sring over serial port
free(outputStr);
}
}
Expanded from the comment above and comments in the previous question.
If you are able to calculate the maximum size of a "packet", then you can avoid dynamic memory all together and just use a fixed buffer size. The calculation doesn't even have to be 100% accurate, as long as it errs on the side of "too big".
e.g.: 5 instances of 5 numbers with a max of 3 digits separated by commas: 5 * 5 * 4 (3 digits + a comma). Not 100% right because the 5th group doesn't need a comma, so you are over estimating by one (or is that 5?). Just be aware of the possible cumulative effect of this if you have multiple "known errors".
So assuming you can estimate the max size, perhaps "encode" it via #defines - perhaps even fixing some of the "known errors").
So now you have char buffer[KNOWN_UPPER_BOUND], as long as you initialize it correctly (e.g. buffer[0] = '\0';, you can just keep appending to it via strcat(). If you were talking big numbers, you could keep track of the last index to avoid repeated scans through the string looking for the end.
e.g. (using globals for simplicity)
char buffer[KNOWN_UPPER_BOUND];
int last_index=0;
addString(char* str)
{
int len = strlen(str);
if (last_index + len > KNOWN_UPPER_BOUND)
{
/* error handling */
}
else
{
strcat(buffer[last_index], str);
last_index += n;
}
}
So what were some of the issues with the dynamic code?
Potential for leaks (much like the errors I mentioned in the calculation - ok (by which I mean 'not overly harmful in a small program') if you leak 2 bytes once, not so good when you put it in a loop and leak 2 bytes over and over again)
Speed issues - malloc is out of you control, it could be very slow. A lot of small allocations can fragment memory which may mean later on when you want a bigger block you can't get one.
Lots of copying and re-copying of data. Your concat is an example here - each concat does a malloc and copies both strings. Every time you call it.
You could still use dynamic memory to hold the final string, but build up each "component" in a fixed size buffer.
What if you move the declaration of char* newStr outside the loop.
Declaring the newStr as char array will be better than pointer to avoid leakage. something like char newStr[50]
looking for some advice on a problem I've been trying to solve for hours.
The program reads from a text file and does some formatting based on commands given within the file. It seems to work for every file I've tried except 2, which are both fairly large.
Here's the offending code:
/* initalize memory for output */
output.data = (char**)calloc(1,sizeof(char*));
/* initialize size of output */
output.size = 0;
/* iterate through the input, line by line */
int i;
for (i = 0; i < num_lines; i++)
{
/* if it is not a newline and if formatting is on */
if (fmt)
{
/* allocate memory for a buffer to hold the line to be formatted */
char *line_buffer = (char*)calloc(strlen(lines[i]) + 1, sizeof(char));
if (line_buffer == NULL)
{
fprintf(stderr, "ERROR: Memory Allocation Failed\n");
exit(1);
}
/* copy the unformatted line into the buffer and tokenize by whitespace */
strcpy(line_buffer, lines[i]);
char* word = strtok(line_buffer, " \n");
/* while there is a word */
while (word)
{
/* if the next word will go over allocated width */
if (current_pos + strlen(word) + 1 > width)
{
/* make ze newline, increase output size */
strcat(output.data[output.size], "\n");
output.size++;
------->>>>> output.data = (char**)realloc(output.data, sizeof(char*) * (output.size + 1));
Using gdb I've figured out the error is on the line with the arrow pointing to it, only thing is I can't figure out why it occurs. It only happens when the text file that is being formatted is large (716 lines), and it seems to happen on the final iteration (num_lines = 716). Any thoughts would be hugely appreciated. Thanks!
EDIT: Sorry folks, should have mentioned that I'm pretty new to this! Fixed some of the errors.
The most immediate problem is:
strncat(output.data[output.size], "\n", 2);
as pointed out by BLUEPIXY. Currently output.data[output.size] is a null pointer 1, so you cannot strncat to it.
To fix this you could allocate some space:
output.data[output.size] = malloc(2);
if ( NULL == output.data[output.size] )
// error handling...
strcpy(output.data[output.size], "\n");
However there might be another solution that fits in better with the rest of your function, which you haven't shown. (Presumably you allocate space somewhere to store word).
It would be helpful to update your post and show the rest of the function. Also make sure you are posting the exact code, as (output.size + ) does not compile. I guess this is a typo you introduced when trying to put those big arrows on your line.
1 Actually it is all bits zero, which is a null pointer on common systems but not guaranteed to be so for all systems.
I am solving a problem on USACO. In the problem, I have to take two strings as inputs and calculate the numerical values modulo 47. If the values are same, then GO is to be printed otherwise STAY has to be printed. The initial numerical value will be calculated by taking the product of the numerical values of the alphabets ( 1 for A and similarily 26 for Z ) and then the final number will be calculated by using modulo.
My program is being compiled withour any error and the first case is also a success. But the problem is in the second case and the way my file is being appended. The program is as follows:-
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MAX 6
main()
{
int cal(char *ptr);
int a,b;
char *comet,*group;
FILE *fptr;
comet=malloc(6*sizeof(char));
group=malloc(6*sizeof(char));
scanf("%s",comet);
a=cal(comet);
scanf("%s",group);
b=cal(group);
fptr=fopen("ride.out","a+"); (1)
//fptr=fopen("ride.txt","a+"); (2)
if(a==b)
fprintf(fptr,"GO\n"); (3)
//printf("GO\n"); (4)
else
fprintf(fptr,"STAY\n"); (5)
//printf("STAY\n"); (6)
fclose(fptr);
return 0;
}
int cal(char *ptr)
{
int c,prod=1,mod;
while(*ptr)
{
c=(*ptr++)-'A'+1;
prod=prod*c;
}
mod=prod%47;
return mod;
}
OUTPUT:-
The first case is the set two strings:-
COMETQ
HVNGAT
and the second case is given in the error notification itself.
If I remove the comment symbols from (2) and put it on (1), then the program is working fine because I can see the contents of the file and they appear just as the grader system wants. It isn't happening for the actual statement of (1). The comments of line (4) and (6) are also fine but not the line (1). I am not able figure this out. Any help?
First a few notes:
main(): a decent main is either:
int main(void)
or
int main(int argc, char *argv[])
Using malloc() you should always check if it returns NULL, aka fail, or not.
Always free() malloc'ed objects.
Everyone has his/hers/its own coding style. I have found this to be invaluable when it comes to C coding. Using it as a base for many other's to. Point being structured code is so much easier to read, debug, decode, etc.
More in detail on your code:
Signature of cal()
First line in main you declare the signature for cal(). Though this works you would probably put that above main, or put the cal() function in entirety above main.
Max length
You have a define #define MAX 6 that you never use. If it is maximum six characters and you read a string, you also have to account for the trailing zero.
E.g. from cplusplus.com scanf:
specifier 's': Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence.
Thus:
#define MAX_LEN_NAME 7
...
comet = malloc(sizeof(char) * MAX_LEN_NAME);
As it is good to learn to use malloc() there is nothing wrong about doing it like this here. But as it is as simple as it is you'd probably want to use:
char comet[MAX_LEN_NAME] = {0};
char group[MAX_LEN_NAME] = {0};
instead. At least: if using malloc then check for success and free when done, else use static array.
Safer scanf()
scanf() given "%s" does not stop reading at size of target buffer - it continues reading and writing the data to consecutive addresses in memory until it reads a white space.
E.g.:
/* data stream = "USACOeRRORbLAHbLAH NOP" */
comet = malloc(szieof(char) * 7);
scanf("%s", buf);
In memory we would have:
Address (example)
0x00000f comet[0]
0x000010 comet[1]
0x000011 comet[2]
0x000012 comet[3]
0x000013 comet[4]
0x000014 comet[5]
0x000015 comet[6]
0x000016 comet[7]
0x000017 /* Anything; Here e.g. group starts, or perhaps fptr */
0x000018 /* Anything; */
0x000019 /* Anything; */
...
And when reading the proposed stream/string above we would not read USACOe in to comet but we would continue reading beyond the range of comet. In other words (might) overwriting other variables etc. This might sound stupid but as C is a low level language this is one of the things you have to know. And as you learn more you'll most probably also learn to love the power of it :)
To prevent this you could limit the read by e.g. using maximum length + [what to read]. E.g:
scanf("%6[A-Z]", comet);
| | |
| | +------- Write to `comet`
| +-------------- Read only A to Z
+---------------- Read maximum 6 entities
Input data
Reading your expected result, your errors, your (N) comments etc. it sound like you should have a input file as well as an output file.
As your code is now it relies on reading data from standard input, aka stdin. Thus you also use scanf(). I suspect you should read from file with fscanf() instead.
So: something like:
FILE *fptr_in;
char *file_data = "ride.in";
int res;
...
if ((fptr_in = fopen(file_data, "r")) == NULL) {
fprintf(stderr, "Unable to open %s for reading.\n", file_data);
return 1; /* error code returned by program */
}
if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", comet)) != 1) {
fprintf(stderr, "Read comet failed. Got %d.\n", res);
return 2;
}
b = cal(comet);
if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", group)) != 1) {
fprintf(stderr, "Read group failed. Got %d.\n", res);
return 2;
}
...
The cal() function
First of, the naming. Say this was the beginning of a project that eventually would result in multiple files and thousand of lines of code. You would probably not have a function named cal(). Learn to give functions good names. The above link about coding style gives some points. IMHO do this in small projects as well. It is a good exercise that makes it easier when you write on bigger to huge ones. Name it e.g. cprod_mod_47().
Then the mod variable (and might c) is superfluous. An alternative could be:
int cprod_mod_47(char *str)
{
int prod = 1;
while (*str)
prod *= *(str++) - 'A' + 1;
return prod % 47;
}
Some more general suggestions
When compiling use many warning and error options. E.g. if using gcc say:
$ gcc -Wall -Wextra -pedantic -std=c89 -o my_prog my_prog.c
This is tremendous amount of help. Further is the use of tools like valgrind and gdb invaluable.
Seems to be a basic question but I would rather ask this to clear up than spend many more days on this.I am trying to copy data in a buffer which I receive(recv call) which will be then pushed to a file. I want to use memcpy to continuously append/add data to the buffer until the size of buffer is not enough to hold more data where I than use the realloc. The code is as below.
int vl_packetSize = PAD_SIZE + (int)p_size - 1; // PAD_SIZE is the size of char array sent
//p_size is the size of data to be recv. Same data size is used by send
int p_currentSize = MAX_PROTO_BUFFER_SIZE;
int vl_newPacketSize = p_currentSize;
char *vl_data = (char *)malloc(vl_packetSize);
memset((char *)vl_data,'\0',vl_packetSize);
/* Allocate memory to the buffer */
vlBuffer = (char *)malloc(p_currentSize);
memset((char *)vlBuffer,'\0',p_currentSize);
char *vlBufferCopy = vlBuffer;
if(vlBuffer==NULL)
return ERR_NO_MEM;
/* The sender first sends a padding data of size PAD_SIZE followed by actual data. I want to ignore the pad hence do vl_data+PAD_SIZE on memcpy */
if((p_currentSize - vl_llLen) < (vl_packetSize-PAD_SIZE)){
vl_newPacketSize +=vl_newPacketSize;
char *vlTempBuffer = (char *)realloc(vlBufferCopy,(size_t)vl_newPacketSize);
if(vlTempBuffer == NULL){
if(debug > 1)
fprintf(stdout,"Realloc failed:%s...Control Thread\n\n",fn_strerror_r(errno,err_buff));
free((void *)vlBufferCopy);
free((void *)vl_data);
return ERR_NO_MEM;
}
vlBufferCopy = vlTempBuffer;
vl_bytesIns = vl_llLen;
vl_llLen = 0;
vlBuffer = vlBufferCopy+vl_bytesIns;
fprintf(stdout,"Buffer val after realloc:%s\n\n",vlBufferCopy);
}
memcpy(vlBuffer,vl_data+PAD_SIZE,vl_packetSize-PAD_SIZE);
/*
fprintf(stdout,"Buffer val before increment:%s\n\n",vlBuffer);
fprintf(stdout,"vl_data length:%d\n\n",strlen(vl_data+PAD_SIZE));
fprintf(stdout,"vlBuffer length:%d\n\n",strlen(vlBuffer));
*/
vlBuffer+=(vl_packetSize-PAD_SIZE);
vl_llLen += (vl_packetSize-PAD_SIZE);
vl_ifNotFlush = 1;
//fprintf(stdout,"Buffer val just before realloc:%s\n\n",vlBufferCopy);
}
Problem: Whan ever I fputs the data into the file later on. Only the first data recv/added to buffer is gets into the file.
Also when I print the value of vlBufferCopy(which points to first location of data returned by malloc or realloc) I get the same result.
If I decrease the size by 1, I see entire data in the file, but it somehow misses the new line character and hence the data is
not inserted in the proper format in the file.
I know it is because of trailing '\0' but some how reducing the size by 1
(vlBuffer+=(vl_packetSize-PAD_SIZE-1);)
misses the new line character. fputs while putting the data removes the trailing null character
Please let me know what I am missing here to check or in the logic
(Note: I tried using strcat:
strcat(vlBuffer,vl_data+PAD_SIZE);
but I wanted to use memcpy as it is faster and also it can be used for any kind of buffer and not only character pointer
Thanks
strcat and memcpy are very different functions.
I suggest you read the documentation of each.
Mainly, there are two differences:
1. memcpy copies data where you tell it to. strcat finds the end of the string, and copies there.
2. memcpy copies the number of bytes you request. strcat copies until the terminating null.
If you're dealing with packets of arbitrary contents, you have no use for strcat, or other string functions.
You need to write to the file in a binary-safe way. Check how to use fwrite instead of fputs. fwrite will copy all the buffer, even if there's a zero in the middle of it.
const char *mybuff= "Test1\0Test2";
const int mybuff_len = 11;
size_t copied = fwrite(mybuff, mybuff_len, 1, output_file);