this code
int
main (void)
{
int i;
char pmbbuf[4];
wchar_t *pwchello = L"1234567890123456789012345678901234567890";
i = wcstombs (pmbbuf, pwchello, wcslen(pwchello)* MB_CUR_MAX + 1);
printf("%d\n", MB_CUR_MAX);
printf (" Characters converted: %u\n", i);
printf (" Multibyte character: %s\n\n", pmbbuf);
return 0;
}
and the strange thing is that it compiles with no warnings.
when I run ./a.out it printed
1
Characters converted: 40
Multibyte character: 1234(
Segmentation fault
Any ideas for the seg fault?
TIA,
cateof
You encounter buffer overrun because you don't null-terminate the buffer after conversion and the buffer size is also not enough to hold the result.
You could allocate memory dynamically since you don't know in advance how much memory is required:
int i;
char pmbbuf*;
wchar_t *pwchello = L"1234567890123456789012345678901234567890";
// this will not write anything, but return the number of bytes in the result
i = wcstombs (0, pwchello, wcslen(pwchello)* MB_CUR_MAX + 1);
//allocate memory - +1 byte for the trailing null, checking for null pointer returned omitted (though needed)
pmbbuf = malloc( i + 1 );
i = wcstombs (pmbbuf, pwchello, wcslen(pwchello)* MB_CUR_MAX + 1);
//put the trailing null
pmbbuf[i] = 0;
//whatever you want to do with the string - print, e-mail, fax, etc.
// don't forget to free memory
free( pmbbuf );
//defensive - to avoid misuse of the pointer
pmbbuf = 0;
You're trying to put a string that's definitely longer than 4 chars into a char array that can hold 4 characters. As you don't specify '4' as the maximum size, the conversion will write into memory that it doesn't own or that might be used by other variables, house keeping data like function return values on the stack or similar. This will lead to the seg fault as you're overwriting data that was pushed on the stack (stacks grow top-down) before you called wcstombs.
Related
I am trying to make this dynamic reallocation work in a portable fashion.
My program accepts a line of text from a user and appends it to a buffer. If the length of text in the buffer is 20 or more, it removes the first 20 characters and moves any characters after that to the start of the buffer.
I have this code which works clean on Linux but when I run it on windows it emits garbage. Does anyone know why/how to make this portable only using malloc. IE not using string.h(strcpy) str... anything but len.
c17 only - no broken stucts(not portable). here is my code. Compiles with no errors gcc 7.3, mingw 7.3. I replace gets and puts with safer functions and I still get garbage on windows. I assume this is a formatting issue...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
void wbuff (message)
char *message;
{
FILE *f = fopen("file.txt", "w");
fprintf(f, "%s", message);
fclose(f);
}
char *rean (message)
char *message;
{
/* performs (write) on buffer, trims lefover, then restores */
char buf[80] = "";
puts("enter a line");
gets(buf);
int bln = strlen( buf );
int mln = strlen( message );
int nln = bln + mln;
printf("new length %d\n", nln);
message = realloc(message, nln);
memmove(message + mln, buf, bln);
/* MISTAKE IS HERE?! */
if( nln >= 20 ) {
int exl = nln -20; // leftover length
char *lo = realloc(NULL, exl); // leftover placeholder
memmove(lo, message+20, exl); // copy leftover
wbuff(message); // write clear buff
message = realloc(NULL, nln);
message = realloc(NULL, exl); // resize buffer
memmove(message, lo, exl); // restore leftover
}
return message;
}
void main (void)
{
char *message = "";
message = realloc(NULL, 0);
while ( 1 == 1 ) {
message = rean( message );
puts(message);
}
return;
}
In C, strings are a sequence of characters terminated by a null byte. You have a number of off-by-one errors here mostly related to not accounting for that fact, as well as memory leaks.
When you first set message in main:
char *message = "";
message = realloc(NULL, 0);
message either is NULL to points to 0 bytes of memory. When you call then call rean the first time:
int mln = strlen( message );
You're either attempting to dereference a NULL pointer to read past the end of allocated memory. You want to allocate at least 1 byte to start and set that byte to 0 so you have an empty string:
char *message = realloc(NULL, 1);
message[0] = '\0';
Then later when you copy the buffer into the message:
message = realloc(message, nln);
memmove(message + mln, buf, bln);
You don't allocate enough space for the terminating null byte, nor do you copy it, so you don't really have a string. When you then try to print it, either puts or printf reads past the end of the allocated memory. You need to allocate 1 extra byte and copy 1 extra byte:
message = realloc(message, nln + 1); // allocate 1 extra byte for the null terminator
memmove(message + mln, buf, bln + 1); // copy 1 extra byte
There are similar issues when you recopy anything past 20 characters:
int exl = nln -20; // leftover length
char *lo = realloc(NULL, exl); // leftover placeholder
memmove(lo, message+20, exl); // copy leftover
wbuff(message); // write clear buff
message = realloc(NULL, nln);
message = realloc(NULL, exl); // resize buffer
memmove(message, lo, exl); // restore leftover
lines 2-3: You don't allocate space for the terminating null byte for lo nor do you copy it.
line 5: You leak the memory previously held by message in the first realloc by passing assigning to message while using NULL as the first argument
line 6-7: You leak the memory allocated in line 5 by doing the same thing. Also, you again don't allocate space for the null byte, nor do you copy it on the next line.
As before, allocate 1 extra byte for each allocation and move 1 extra byte to account for the null terminator. Also, free lo at the end of the block, remove the extra realloc for message, and pass the prior value of message to realloc so you don't leak memory:
int exl = nln -20;
char *lo = realloc(NULL, exl + 1); // allocate 1 extra byte
memmove(lo, message+20, exl + 1); // copy 1 extra byte
wbuff(message);
// remove extra realloc
message = realloc(message, exl + 1); // pass in old message, allocate 1 extra
memmove(message, lo, exl + 1); // copy 1 extra byte
free(lo); // free leftover
These issues of reading and writing past the end of allocated memory all invoke undefined behavior, which explains why you see different results on different operating systems.
As far as conforming code goes, use fgets intead of gets:
fgets(line, sizeof(line), stdin);
This function will include a newline in line if there's space for it, so be sure to remove it if that's the case.
Also change main to return int, and remove #include <malloc.h> since the malloc family of function is defined to reside in stdlib.h.
Had you used strcpy and strcat instead of memmove, you wouldn't have had to account for copying the null terminating byte as those functions do that for you. You would still however need to account for that when allocating memory. There's also no conflict between strcpy, malloc, and realloc, as they are all part of the standard and work together properly. Using them together is no problem. If they aren't working properly for you then you aren't using them correctly.
After applying my updates, you can replace this:
memmove(message + mln, buf, bln + 1);
With this:
strcat(message, buf);
And replace this:
memmove(lo, message+20, exl + 1); // copy leftover
...
memmove(message, lo, exl + 1); // restore leftover
With this:
strcpy(lo, message+20);
...
strcpy(message, lo);
And it will still work properly and be conforming.
I'm facing an issue connected with printing one char from string in c.
The function takes from users two variables - number (number which should print character from string) and string. When I put as a string "Martin" and number is 5 then the output is "i". But when the number is larger than the string length something goes wrong and I actually don't know what's wrong.
PS. If the number is longer than string size it should print "Nothing".
void printLetter() {
char * string = (char*)malloc(sizeof(char));
int n;
printf("Number:\n");
scanf("%i", &n);
printf("String:\n");
scanf("%s", string);
if(n > strlen(string)) {
printf("nothing");
} else {
printf("%c\n", string[n+1]);
}
free(string);
}
There is no need for dynamic allocation here, since you do not know the length of the string in advance, so just do:
void printLetter() {
char string[100]; // example size 100
...
scanf("%99s", string); // read no more than your array can hold
}
A fun exercise would be to count the length of the string, allocate dynamically exactly as mush space as you need (+1 for the null terminator), copy string to that dynamically allocated space, use it as you wish, and then free it.
Moreover this:
printf("%c\n", string[n+1]);
should be written as this:
printf("%c\n", string[n-1]);
since you do not want to go out bounds of your array (and cause Undefined Behavior), or print two characters next of the requested character, since when I ask for the 1st character, you should print string[0], when I ask for the 2nd, you should print string[1], and so on. So you see why we need to print string[n-1], when the user asks for the n-th letter.
By the way, it's common to use a variable named i, and not n as in your case, when dealing with an index. ;)
In your code, this:
char * string = malloc(sizeof(char));
allocates memory for just one character, which is no good, since even if the string had one letter only, where would you put the null terminator? You know that strings in C should (almost) always be NULL terminated.
In order to allocate dynamically memory for a string of size N, you should do:
char * string = malloc((N + 1) * sizeof(char));
where you allocate space for N characters, plus 1 for the NULL terminator.
Couple of problems...
sizeof(char) is generally 1 byte. Hence malloc() is allocating only one byte of memory to string. Perhaps a larger block of memory is required? "Martin", for example, will require at least 6 bytes, plus the string termination character (seven bytes total).
printf("%c\n", string[n+1]) is perhaps not quite right...
String: Martin\0
strlen= 6
Offset: 0123456
n = 5... [n+1] = 6
The character being output is the string terminator '\0' at index 6.
This might work better:
void printLetter() {
char * string = malloc(100 * sizeof(char));
int n;
printf("Number:\n");
scanf("%i", &n);
printf("String:\n");
scanf("%s", string);
if(n > strlen(string)) {
printf("nothing");
} else {
printf("%c\n", string[n-1]);
}
free(string);
}
You are facing buffer overflow.
Take a look to this question, so it will show you how to manage your memory properly in such situation: How to prevent scanf causing a buffer overflow in C?
Alternatively you can ask for number of letter and allocate only that much memory + 1. Then fgets(string, n,stdin); because you don't need rest of the string :-)
I'm using the code below to add some "0" chars into my string, but it seems there is a problem and the program will crash. Everything seems logic but I do not know where is the problem?
#include <stdlib.h>
#include <string.h>
int main()
{
char *Ten; int i=0; Ten = malloc(12);
Ten="1";
for (i=0;i<10;i++)
strcat(Ten,"0");
printf("%s",Ten);
return 0;
}
You declare Ten as a pointer to a string literal. However, you cannot rely on being able to modify a string literal, and thus the program crashes.
To fix this, you can declare Ten as an array instead:
int main()
{
char Ten[12]="1"; int i=0;
for (i=0;i<10;i++)
strcat(Ten,"0");
printf("%s",Ten);
return 0;
}
Note that you need 12 bytes; 11 for the characters and one for the terminating NUL character.
Ten is a string literal and you cannot modify it. Try with array instead
char Ten[12] = "1";
for (i=0;i<10;i++)
strcat(Ten,"0");
printf("%s",Ten);
notice that I created an array of 12 characters, because there should be room for a termination '\0'.
You actually don't need strcat here, it's just do this
char Ten = malloc(12);
if (Ten != NULL)
{
Ten[0] = '1';
for (i = 1 ; i < 11 ; i++)
Ten[i] = '0';
Ten[11] = '\0';
/* Use Ten here, for example printf it. */
printf("%s",Ten);
/* You should release memory. */
free(Ten);
}
or
char Ten = malloc(12);
if (Ten != NULL)
{
Ten[0] = '1';
memset(Ten + 1, '0', 10);
Ten[11] = '\0';
/* Use Ten here, for example printf it. */
printf("%s",Ten);
/* You should release memory. */
free(Ten);
}
To quote from strcat manual on linux:
The strcat() function appends the src string to the dest string,
overwriting the terminating null byte ('\0') at the end of dest, and
then adds a terminating null byte. The strings may not overlap, and
the dest string must have enough space for the result. If dest is not
large enough, program behavior is unpredictable; buffer overruns are
a favorite avenue for attacking secure programs.
Your Ten array is only long enough to store original literal. You need to preallocate memory as long as final desired string.
String literals might be stored in read only section of memory. Any attempt to modify such a literal causes undefined behavior.
To concatenate two strings, the destination must have enough space allocated for the characters to be added and space for '\0'. Change the declaration of Ten to
char Ten[12] = "1";
and it will work.
I'm trying to print out a memory address backwards like in a little endian machine, but for some reason my program is outputting both char arrays in my program.
char address[8];
char result[16];
scanf("%s", address);
int x = 1;
for(x; x<5; x++)
{
int y = 4*x;
int z = 8 - 2*x;
result[y-4] = '\\';
result[y-3] = 'x';
result[y-2] = address[z];
result[y-1] = address[z+1];
}
printf("%s", result);
for example, if I input "12345678" it outputs "\x78\x56\x34\x1212345678"
I want the "\x78\x56\x34\x12", but I don't understand why the "12345678" is also printed.
You forget to terminate the string. In fact, your array is to small for the terminator, you need it to be 17 characters, then do result[16] = '\0';.
Oh, and you have an out-of-bound error in your code as well, as if you enter 8 characters for the input then scanf will write 9 characters to the 8-character array.
%s //
in C %s expects a pointer to a string, unlike %d or %c(these expect variables with integers or characters), so your pointer must be null terminated, so the program knows where in memory it must stop printing, or else your are printing random stuff in memory
I'm coding a program that takes some files as parameters and prints all lines reversed. The problem is that I get unexpected results:
If I apply it to a file containing the following lines
one
two
three
four
I get the expected result, but if the file contains
september
november
december
It returns
rebmetpes
rebmevons
rebmeceds
And I don't understand why it adds a "s" at the end
Here is my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void reverse(char *word);
int main(int argc, char *argv[], char*envp[]) {
/* No arguments */
if (argc == 1) {
return (0);
}
FILE *fp;
int i;
for (i = 1; i < argc; i++) {
fp = fopen(argv[i],"r"); // read mode
if( fp == NULL )
{
fprintf(stderr, "Error, no file");
}
else
{
char line [2048];
/*read line and reverse it. the function reverse it prints it*/
while ( fgets(line, sizeof line, fp) != NULL )
reverse(line);
}
fclose(fp);
}
return (0);
}
void reverse(char *word)
{
char *aux;
aux = word;
/* Store the length of the word passed as parameter */
int longitud;
longitud = (int) strlen(aux);
/* Allocate memory enough ??? */
char *res = malloc( longitud * sizeof(char) );
int i;
/in this loop i copy the string reversed into a new one
for (i = 0; i < longitud-1; i++)
{
res[i] = word[longitud - 2 - i];
}
fprintf(stdout, "%s\n", res);
free(res);
}
(NOTE: some code has been deleted for clarity but it should compile)
You forget to terminate your string with \0 character. In reversing the string \0 becomes your first character of reversed string. First allocate memory for one more character than you allocated
char *res = malloc( longitud * sizeof(char) + 1);
And the try this
for (i = 0; i < longitud-1; i++)
{
res[i] = word[longitud - 2 - i];
}
res[i] = '\0'; // Terminating string with '\0'
I think I know the problem, and it's a bit of a weird issue.
Strings in C are zero terminated. This means that the string "Hi!" in memory is actually represented as 'H','i','!','\0'. The way strlen etc then know the length of the string is by counting the number of characters, starting from the first character, before the zero terminator. Similarly, when printing a string, fprintf will print all the characters until it hits the zero terminator.
The problem is, your reverse function never bothers to set the zero terminator at the end, which it needs to since you're copying characters into the buffer character by character. This means it runs off the end of your allocated res buffer, and into undefined memory, which just happened to be zero when you hit it (malloc makes no promises of the contents of the buffer you allocate, just that it's big enough). You should get different behaviour on Windows, since I believe that in debug mode, malloc initialises all buffers to 0xcccccccc.
So, what's happening is you copy september, reversed, into res. This works as you see, because it just so happens that there's a zero at the end.
You then free res, then malloc it again. Again, by chance (and because of some smartness in malloc) you get the same buffer back, which already contains "rebmetpes". You then put "november" in, reversed, which is slightly shorter, hence your buffer now contains "rebmevons".
So, the fix? Allocate another character too, this will hold your zero terminator (char *res = malloc( longitud * sizeof(char) + 1);). After you reverse the string, set the zero terminator at the end of the string (res[longitud] = '\0';).
there are two errors there, the first one is that you need one char more allocated (all chars for the string + 1 for the terminator)
char *res = malloc( (longitud+1) * sizeof(char) );
The second one is that you have to terminate the string:
res[longitud]='\0';
You can terminate the string before entering in the loop because you know already the size of the destination string.
Note that using calloc instead of malloc you will not need to terminate the string as the memory gets alreay zero-initialised
Thanks, it solved my problem. I read something about the "\0" in strings but wasn't very clear, which is now after reading all the answers (all are pretty good). Thank you all for the help.