I've made use of an array, and want to delete the content by placing null in array[0] but it doesn't work. Example... If I type Jesper, then the serial.print(nameBuffer[1]) returns e.
A temporary solution I use is a for-loop to place null in all it's spaces.
char name1[9] = "Jesper";
char nameBuffer[9];
void setup()
{
Serial.begin(9600);
}
void loop()
{
int i = 0;
nameBuffer[0] = 0;
Serial.print(nameBuffer[1]);
Serial.println(" All reset\n");
while(Serial.available() == 0)
{
// wait for data to be send
}
while(Serial.available() > 0)
{
int inByte = Serial.read();
delay(50);
nameBuffer[i] = char(inByte);
i++;
Serial.print("Recieved bytes: ");
Serial.println(inByte,DEC);
}
Serial.print("Searching for: ");
Serial.println(nameBuffer);
}
nameBuffer is an array of 9 char elements. Each of those elements has a value of type char.
Setting a char object to 0 doesn't remove it from the array (0 or '\0', the null character, is as valid a char value as any other), nor does it affect the elements that follow it.
Now if you're treating the contents of nameBuffer as a string (defined by the C standard as "a contiguous sequence of characters terminated by and including the first null
character"), then storing '\0' in nameBuffer[0] will cause it to contain an empty string. It has a length of 0, but there are still 9 char values stored in the array. So this:
printf("%s", nameBuffer);
won't print anything, but namebuffer[1] is still a valid char object holding whatever value was last stored in it.
Don't assume that printing a null character, or sending it over a serial port, will do nothing. If you don't want to print each character in your array, you'll need some logic to avoid printing the characters you don't want.
Incidentally, your code appears to be C++, not C. You have overloaded versions of Serial.print, one taking a char argument and one taking a char*; C doesn't support overloading. And char(inByte) is C++; it's a syntax error in C. (BTW, a cast isn't necessary there; the value will be converted implicitly.)
Related
This is my code for two functions in C:
// Begin
void readTrain(Train_t *train){
printf("Name des Zugs:");
char name[STR];
getlinee(name, STR);
strcpy(train->name, name);
printf("Name des Drivers:");
char namedriver[STR];
getlinee(namedriver, STR);
strcpy(train->driver, namedriver);
}
void getlinee(char *str, long num){
char c;
int i = 0;
while(((c=getchar())!='\n') && (i<num)){
*str = c;
str++;
i++;
}
printf("i is %d\n", i);
*str = '\0';
fflush(stdin);
}
// End
So, with void getlinee(char *str, long num) function I want to get user input to first string char name[STR] and to second char namedriver[STR]. Maximal string size is STR (30 charachters) and if I have at the input more than 30 characters for first string ("Name des Zuges"), which will be stored in name[STR], after that I input second string, which will be stored in namedriver, and then printing FIRST string, I do not get the string from the user input (first 30 characters from input), but also the second string "attached" to this, I simply do not know why...otherwise it works good, if the limit of 30 characters is respected for the first string.
Here my output, when the input is larger than 30 characters for first string, problem is in the row 5 "Zugname", why I also have second string when I m printing just first one...:
Name des Zugs:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
i is 30
Name des Drivers:xxxxxxxx
i is 8
Zugname: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxxxxxxxx
Drivername: xxxxxxxx
I think your issue is that your train->name is not properly terminated with '\0', as a consequence when you call printf("%s", train->name) the function keeps reading memory until it finds '\0'. In your case I guess your structure looks like:
struct Train_t {
//...
char name[STR];
char driver[STR];
//...
};
In getlinee() function, you write '\0' after the last character. In particular, if the input is more than 30 characters long, you copy the first 30 characters, then add '\0' at the 31-th character (name[30]). This is a first buffer overflow.
So where is this '\0' actually written? well, at name[30], even though your not supposed to write there. Then, if you have the structure above when you do strcpy(train->name, name); you will actually copy a 31-bytes long string: 30 chars into train->name, and the '\0' will overflow into train->driver[0]. This is the second buffer overflow.
After this, you override the train->driver buffer so the '\0' disappears and your data in memory basically looks like:
train->name = "aaa...aaa" // no '\0' at the end so printf won't stop reading here
train->driver = "xxx\0" // but there
You have an off-by-one error on your array sizes -- you have arrays of STR chars, and you read up to STR characters into them, but then you store a NUL terminator, requiring (up to) STR + 1 bytes total. So whenever you have a max size input, you run off the end of your array(s) and get undefined behavior.
Pass STR - 1 as the second argument to getlinee for the easiest fix.
Key issues
Size test in wrong order and off-by-one. ((c=getchar())!='\n') && (i<num) --> (i+1<num) && ((c=getchar())!='\n'). Else no room for the null character. Bad form to consume an excess character here.
getlinee() should be declared before first use. Tip: Enable all compiler warnings to save time.
Other
Use int c; not char c; to well distinguish the typical 257 different possible results from getchar().
fflush(stdin); is undefined behavior. Better code would consume excess characters in a line with other code.
void getlinee(char *str, long num) better with size_t num. size_t is the right size type for array sizing and indexing.
int i should be the same type as num.
Better code would also test for EOF.
while((i<num) && ((c=getchar())!='\n') && (c != EOF)){
A better design would return something from getlinee() to indicate success and identify troubles like end-of-file with nothing read, input error, too long a line and parameter trouble like str == NULL, num <= 0.
I believe you have a struct similar to this:
typedef struct train_s
{
//...
char name[STR];
char driver[STR];
//...
} Train_t;
When you attempt to write a '\0' to a string that is longer than STR (30 in this case), you actually write a '\0' to name[STR], which you don't have, since the last element of name with length STR has an index of STR-1 (29 in this case), so you are trying to write a '\0' outside your array.
And, since two strings in this struct are stored one after another, you are writing a '\0' to driver[0], which you immediately overwrite, hence when printing out name, printf doesn't find a '\0' until it reaches the end of driver, so it prints both.
Fixing this should be easy.
Just change:
while(((c=getchar())!='\n') && (i<num))
to:
while(((c=getchar())!='\n') && (i<num - 1))
Or, as I would do it, add 1 to array size:
char name[STR + 1];
char driver[STR + 1];
I'm trying to add (well, append really) the letters in the alphabet to an empty char array. However, I appear to run into some sort of pointer issue I don't understand, as my array contains only the last character. I tried moving the letter char outside of the for loop, but the compiler didn't like that. I also looked on here about how to create a list of all alphabetical chars, and one of the better answers was to type them all in 1 at a time. However, my problem means I don't fully understand for loops and pointers in C, and I want to.
#include <stdio.h>
int main(void) {
char *empty_list[26];
for (int i = 0; i < 26; i++){
char letter = i + 65;
empty_list[i] = &letter;
}
printf("%s", *empty_list);
return 0;
}
The main problem is your declaration:
char *empty_list[26];
defines an array of 26 pointers to characters. In your current code you assign each element in the array the address of the variable letter. Since that is out of scope when you print it is luck that it prints out the last one, it could equally have printed out garbage or crashed if the code between was complex. It could also have printed out additional garbage after the letter with what you already have since there is no way of knowing whether there is a string terminating character (\0) after the letter. In your existing code printf("%s", *empty_list); prints the first pointer from the array as a null terminated string, which if you ignore the loss of scope and assume the memory contents are still around, will be the last value from the loop since all pointers in your array point to the memory that letter was stored at and that memory has the last value from the loop.
If your intention was to create an array with the letters then it should be:
char empty_list[27];
It needs to be 27 as you need to leave space for the string terminating character at the end. One way to fill that in would be to use:
empty_list[26] = '\0';
after the end of your for loop and before you print the contents of the array (do not include the asterisk here - because it is an array the compiler will automatically take the address of the first element):
printf("%s", empty_list);
As brothir mentioned in the comments when you assign the value of the letter to the element in the array it should be without the ampersand:
empty_list[i] = letter;
There are a few things wrong with your code.
Firstly, the type of empty_list is presently an array of pointers to char, when it really should be an array of char, since your intent is to print it out as if it were the latter in the call to printf after your loop. char empty_list[26]; is the correct declaration.
Secondly, in your loop, you assign &letter when all you need is letter. Heck, you don't even need the intermediate variable letter. Just empty_list[i] = i + 'A'; will suffice.
Lastly, you are passing empty_list to printf to satisfy a format specifier %s, which expects a null-terminated string. What you need to do is add another element to empty_list and set that to zero:
char empty_list[27];
// populated 0..25 with 'A'..'Z' in your code...
empty_list[26] = '\0';
printf("%s\n", empty_list);
// Output: ABC...Z
With the above help (much appreciated), my working code to create an array of letters in C is below:
#include <stdio.h>
int main(void) {
// create an array with 1 extra space for null terminator
char empty_list[27];
// add null terminator so string knows when it's finished.
empty_list[26] = '\0';
for (int i = 0; i < 26; i++){
// add 65 to get ASCII value for 'A'
char letter = A + i;
// insert each char into the array sequentially
empty_list[i] = letter;
}
printf("%s", empty_list);
return 0;
}
Hi I have been reading for hours and still can't grasp the conversions between
{
char i ="adf";
char foo[];
char bar[256];
}
and adding * and & makes it more confusing
I have some code that is working.
int TX_SEND(char send[])
{
unsigned char *p_tx_buffer;
p_tx_buffer = &send[0];
strcat(send, "\r");
// Write to the port
int n = write(fd,&send[0],3);
if (n < 0) {
perror("Write failed - ");
return -1;
}
return(0);
}
code is working but I need help with 2 parts.
I want to be able to run this function like kind of like printf IE TX_SEND("AT+CGMSD=STUFF"); but I am stuck
but before hand I do this alot.
char txsend[] = "at";
TX_SEND(txsend);
Also inside my TX_WRITE() I am using write(fd,&send[0],3), but it is hardcoded to send 3 bytes from send[]. I want this to be dynamic so I can just send strings at any length (realistically they will be less than 300 ASCII chars always). I tried to do something with a pointer in there but gave up (*p_tx_buffer was my beginning attempt).
i think you want
int TX_SEND(char *send)
{
int n = write(fd,send,strlen(send));
if (n < 0) {
perror("Write failed - ");
return -1;
}
return(0);
}
you cannot tack on \n to send with strcat. I would add it in the calling function, or declare an intermediate buffer and sprintf to it
like this
int TX_SEND(char *send)
{
char buff[50]; // i dont know a good max size
snprintf(buff, sizeof(buff), "%s\n", send);
int n = write(fd,buff,strlen(buff));
if (n < 0) {
perror("Write failed - ");
return -1;
}
return(0);
}
I'm not going to go through your code line-by-line, but I urge you to focus on these facts:
chars are chars and strings are strings, and never the twain shall meet. (They're totally different.)
'x' is a character constant.
"x" is a string constant.
A string is an array of characters (terminated by '\0').
When you mention an array (including a string) in a context where you need its value, what you get is a pointer to the array's first element.
When you put a & in front of something, what you get is a pointer to that something.
When you put a * in front of a pointer, what you get is the thing that the pointer points to.
Putting this together, we could write
char str[] = "xyz";
char *p = str; /* per rule 5, this is fine, and p gets a pointer to str's first element */
char c = *p; /* per rule 7, c gets the first character of str, which is 'x' */
printf("%c\n", c);
If you're just starting with C, you may not have come across rule 5 yet. It will probably surprise you at first. Learn it well, though: you'll never understand arrays and pointers in C without it.
I am learning C and a I came across this function in my study materials. The function accepts a string pointer and a character and counts the number of characters that are in the string. For example for a string this is a string and a ch = 'i' the function would return 3 for 3 occurrences of the letter i.
The part I found confusing is in the while loop. I would have expected that to read something like while(buffer[j] != '\0') where the program would cycle through each element j until it reads a null value. I don't get how the while loop works using buffer in the while loop, and how the program is incremented character by character using buffer++ until the null value is reached. I tried to use debug, but it doesn't work for some reason. Thanks in advance.
int charcount(char *buffer, char ch)
{
int ccount = 0;
while(*buffer != '\0')
{
if(*buffer == ch)
ccount++;
buffer++;
}
return ccount;
}
buffer is a pointer to a set of chars, a string, or a memory buffer holding char data.
*buffer will dereference the value at buffer, as a char. This can be compared with the null character.
When you add to buffer - you are adding to the address, not the value it points to, buffer++ adds 1 to the address, pointing to the next char. This means that now *buffer results in the next character.
In the loop you are incrementing the pointer buffer until it points to the null character, at which point you know you scanned the whole string. Instead of buffer[j], which is equivalent to *(buffer+j), we are incrementing the pointer itself.
When you say buffer++ you increment the address stored in buffer by one.
Once you internalize how pointers work, this code is cleaner than the code that uses a separate index to scan the character string.
In C and C++, arrays are stored in sequence, and an array is stored according to its first address and length.
Therefore *buffer is actually the address of the first byte, and is synonymous with buffer[0]. Because of this, you can use buffer as an array, like this:
int charcount(char *buffer, char ch)
{
int ccount = 0;
int charno = 0;
while(buffer[charno] != '\0')
{
if(buffer[charno] == ch)
ccount++;
charno++;
}
return ccount;
}
Note that this works because strings are null terminated - if you don't have a null termination in the character array pointed to by *buffer it will continue reading forever; you lose the bit where c knows how long the array is. This is why you see so many c functions to which you pass a pointer and a length - the pointer tells it the [0] position of the array, and the size you specify tells it how far to keep reading.
Hope this helps.
I have this database and I Need to check whether a Product Name is already in the database otherwise I ask the user to input another one.
The problem is this:
I'm trying to compare a string (the Product Name) found inside the struct with the string the user inputs.
The coding of the struct, the user input part and the search method are here below:
product Structure
typedef struct
{
char pName[100];
char pDescription [100];
float pPrice;
int pStock;
int pOrder;
}product;
the checkProduct method:
int checkProduct (char nameCheck[100])
{
product temp;
p.pName = nameCheck;
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName))
{
return 1;
}
}
return 0;
}
and the user input part [part of the code]:
char nameCheck[100];
gets (nameCheck);
checkProduct (nameCheck);
while (checkProduct == 1)
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
{
continue;
}
}
p.pName = nameCheck;
Now I am having the following errors (I Use ECLIPSE):
on the line
while (checkProduct == 1) [found in the user input] is giving me:
"comparison between pointer and integer - enabled by default" marked by a yellow warning triangle
p.pName = nameCheck; is marked as a red cross and stopping my compiling saying:
"incompatible types when assigning to type 'char [100] from type 'char*'
^---- Is giving me trouble BOTH in the userinput AND when I'm comparing strings.
Any suggestions how I can fix it or maybe how I can deference it? I can't understand why in the struct the char pName is being marked as '*' whereas in the char[100] it's not.
Any brief explanation please?
Thank you in advance
EDIT: After emending the code with some of below:
THIS Is the INPUT NAME OF PRODUCT section;
char *nameCheck;
nameCheck = "";
fgets(nameCheck,sizeof nameCheck, stdin);
checkProduct (nameCheck);
int value = checkProduct (nameCheck);
while (value == 1)
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
{
}
}
strcpy (p.pName, nameCheck);
this is the new checkName method
int checkProduct (char *nameCheck)
{
product temp;
strcpy (p.pName, nameCheck);
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName) == 0)
{
return 1;
}
}
return 0;
}
p.pName = nameCheck;
is wrong as you try to assign address of one array to another. What you probably want is to copy it.
Use strcpy() instead.
strcpy(p.pName, nameCheck);
while (checkProduct == 1)
Since checkProduct is a function, the above condition will always be false as the address of function won't be equal to 1. You can store the return value in another integer like this:
int value = checkProduct(nameCheck);
while (value == 1)
/* rest of the code */
Or rather simply:
while ( checkProduct(nameCheck) == 1 ) {
...
Note - I've not checked entire code, there might be other bugs apart from this one. Btw, if you are new to programming, you can start with small examples from textbooks and then work towards slightly complex stuff.
int checkProduct (char nameCheck[100])
Note that the type signature is a lie. The signature should be
int checkProduct(char *nameCheck)
since the argument the function expects and receives is a pointer to a char, or, to document it for the user that the argument should be a pointer to the first element of a 0-terminated char array
int checkProduct(char nameCheck[])
Arrays are never passed as arguments to functions, as function arguments, and in most circumstances [the exceptions are when the array is the operand of sizeof, _Alignof or the address operator &] are converted to pointers to the first element.
{
product temp;
p.pName = nameCheck;
Arrays are not assignable. The only time you can have an array name on the left of a = is initialisation at the point where the array is declared.
You probably want
strcpy(p.pName, nameCheck);
there.
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName))
strcmp returns a negative value if the first argument is lexicographically smaller than the second, 0 if both arguments are equal, and a positive value if the first is lexicographically larger than the second.
You probably want
if (strcmp(temp.pName, p.pName) == 0)
there.
gets (nameCheck);
Never use gets. It is extremely unsafe (and has been remoed from the language in the last standard, yay). Use
fgets(nameCheck, sizeof nameCheck, stdin);
but that stores the newline in the buffer if there is enough space, so you have to overwrite that with 0 if present.
If you are on a POSIX system and don't need to care about portability, you can use getline() to read in a line without storing the trailing newline.
checkProduct (nameCheck);
You check whether the product is known, but throw away the result. Store it in a variable.
while (checkProduct == 1)
checkProduct is a function. In almost all circumstances, a function designator is converted into a pointer, hence the warning about the comparison between a pointer and an integer. You meant to compare to the value of the call you should have stored above.
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
You read in characters without storing them. So you will never change the contents of nameCheck, and then be trapped in an infinite loop.
{
continue;
}
If the only statement in a loop body is continue;, you should leave the body empty.
}
p.pName = nameCheck;
Once again, you can't assign to an array.
Concerning the edit,
char *nameCheck;
nameCheck = "";
fgets(nameCheck,sizeof nameCheck, stdin);
you have changed nameCheck from an array to a pointer. That means that sizeof nameCheck now doesn't give the number of chars you can store in the array, but the size of a pointer to char, which is independent of what it points to (usually 4 on 32-bit systems and 8 on 64-bit systems).
And you let that pointer point to a string literal "", which is the reason for the crash. Attempting to modify string literals is undefined behaviour, and more often than not leads to a crash, since string literals are usually stored in a read-only segment of the memory nowadays.
You should have left it at
char nameCheck[100];
fgets(nameCheck, sizeof nameCheck, stdin);
and then you can use sizeof nameCheck to tell fgets how many characters it may read, or, alternatively, you could have a pointer and malloc some memory,
#define NAME_LENGTH 100
char *nameCheck = malloc(NAME_LENGTH);
if (nameCheck == NULL) {
// malloc failed, handle it if possible, or
exit(EXIT_FAILURE);
}
fgets(nameCheck, NAME_LENGTH, stdin);
Either way, after getting input, remove the newline if there is one:
size_t len = strlen(nameCheck);
if (len > 0 && nameCheck[len-1] == '\n') {
nameCheck[len-1] = 0;
}
// Does windows also add a '\r' when reading from stdin?
if (len > 1 && nameCheck[len-2] == '\r') {
nameCheck[len-2] = 0;
}