I want make array which will be declared dynamically I imagine something like this. I want make program that recognize characters in the word.
char i;
scanf("%c",&i);
char word[]=i;
printf("%c",word[0]);
I also tried something like this:
char i;
scanf(%c,&i);
char *word=i;
printf("%c",word[0]);
I have no clue how to make it work.
Let's just start with the basics. What you are declaring when your write:
char word[]=i;
is an array of characters which will be initialized based on what is to the right of the = sign. In this case to i which is a single character (even though that technically is an invalid initializer). So essentially you are attempting to declare a character array of size 1.
If that were allowed, word would not a character string because it was not (and could not be) null-terminated. It is simply an array of type char and cannot be used as a string. (note char word[]="i"; would work - and be null-terminated)
When you declare word as:
char *word=i;
you have created a pointer to type char that is incorrectly initialized to hold the memory address of i.
(a pointer simply contains, as its value, the address for something else stored in memory, in this case you have set that address to the value of i which likely is in the system reserved range of memory and will probably cause an immediate segmentation fault.
What you probably intended was:
char *word=&i;
Where you store the address of i as the value of word. While this will give you a character pointer to i, it has nothing to do with dynamic allocation beyond involving a pointer.
To properly allocate word dynamically, you do still need to create a pointer named word, but you then need to allocate a block of memory that word will point to. (pointing to the starting address for that block of memory). So to dynamically allocate space for word you would expect to see something like:
char *word = NULL; /* initialization to NULL is good practice */
word = malloc (numbytes * sizeof *word); /* allocate numbytes of storage */
You then have a pointer word whose value is the starting address of the new block of memory of (numbytes x sizeof *word) bytes in size. (since sizeof *word is simply the sizeof (char) its value is 1). So you have created a block of memory numbytes in size.
You have two responsibilities regarding that block of memory (1) you must preserve a pointer to the starting address of that block; so (2) you can free the memory when it is no longer needed. (1) means no word++;, etc.. in your code. If you need to iterate a pointer value, create a pointer, e.g.:
char *p = word;
Then you can use pointer arithmetic on p without effecting the value of word.
You can then fill word any way you like as long as you leave room for a null-terminator at the end (if you are going to use it as a string) and don't attempt to write more than numbytes worth of data to it.
So in your case, if your intent was to dynamically allocate word to hold the character i to be printed as a string, you could easily do:
char *word = NULL;
word = malloc (10 * sizeof *word);
if (!word) { /* always validate each allocation */
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
if (scanf ("9%s", word))
printf ("word contains '%s'\n", word);
free (word);
Note: that when using scanf ("9%s", word), you will stop reading as soon as the first space is encountered. You may want to use scanf ("9%[^\n]%*c", word) which essentially says %9[^\n] read up to 9 characters (that do NOT include a \n), then %*c read and discard the newline without adding it to the match count returned by scanf. So your test:
if (scanf ("9%s", word))
insures scanf has read at least one character into word as a string (null-terminating word) which on successful read is added to the match count for scanf making the match count = 1. The match count is then returned by scanf insuring that you only print after a successful read.
Note also: the number of character scanf will read is limited to 9 by including a width specifier %9s (or %9[^\n]) in the format string. This insures you cannot write beyond the end of the memory allocated to word while guaranteeing space available to null-terminate the string.
One other nit. If you are expecting a user to enter data, then prompt the user to enter the data, so the user isn't left looking at a blinking cursor on the screen, wondering if the program got hung up, etc.. Putting all this together in a short example.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *word = NULL;
word = malloc (10 * sizeof *word);
if (!word) { /* always validate each allocation */
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
printf ("\nEnter up to 9 characters to store in 'word': ");
if (scanf ("%9[^\n]%*c", word))
printf ("word contains '%s'\n", word);
free (word);
return 0;
}
Use/Output
$ ./bin/dynalloc
Enter up to 9 characters to store in 'word': dog
word contains 'dog'
$ ./bin/dynalloc
Enter up to 9 characters to store in 'word': 1234567890
word contains '123456789'
$ ./bin/dynalloc
Enter up to 9 characters to store in 'word': my dog
word contains 'my dog'
Let me know if you have any questions.
Related
The goal is storing the adress of a string litteral in a char*, which is a member of struct
id. I thought about using an array.
The problem with array is that, if I set the maximum number of character to 7, the user
might enter less than 7, so it will be a waste of memory.
The advantage using getchar() is that I can set max of char to 7, but if user enter less, that's ok too.
typedef struct id
{
int age;
char* name;
}id;
id Mary;
char L;
int c =0;
printf("Enter your age: ");
scanf("%d",&Mary.age);
printf(" Enter your name: );
if( (L=getchar() != '\n' )
{
// stroring string litteral in char*
}
printf("%s", Mary.name);
This is a common problem: "How do I read an input string of unknown length?" Daniel Kleinstein has mentioned several general solutions in his answer. I'll give a more implementation-based answer here
Firstly, your program does not try to store a string literal, but a string read from an input stream (e.g. stdin).
Secondly, it is not possible to store a string "in a char*". The string is stored in memory pointed to by a char*. This memory needs to be allocated first.
The following code comes closest to what you want to do. It reads one character at a time and increases the size of the memory copied to by 1 byte every time.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int age;
char *name;
} Id;
int main(void)
{
Id mary;
printf(" Enter your name: ");
size_t nameSize = 0U;
mary.name = NULL;
while (true)
{
mary.name = (char*) realloc(mary.name, ++nameSize); // cast is optional
if (mary.name == NULL)
{
printf("Memory allocation error\n");
exit(EXIT_FAILURE);
}
int ch = getchar(); // Note the `int` type, necessary to detect EOF
if (ch == '\n' || ch == EOF)
{
mary.name[nameSize - 1] = '\0';
break;
}
mary.name[nameSize - 1] = (char) ch;
}
printf("%s\n", mary.name);
free(mary.name);
}
This does not waste a single byte of memory, however, the frequent memory reallocations will make this code slow. A good compromise is to read one fixed length string at a time, instead of one character at a time.
To do this in practice: create a buffer on the stack of fixed length (e.g. 64 characters), read into that buffer using fgets, and copy that contents to mary.name. If the string didn't fit the buffer, repeatedly call fgets again, realloc mary.name and append the contents of the buffer to mary.name until you find a newline character.
Another, simpler solution is to set a maximum length for the string, allocate memory for that length, read a string of maximally that length, and finally reallocate the memory to the (possibly smaller) actual size of the string.
There isn't a magic bullet solution for this sort of problem.
Your options are:
Use an array with a maximum length - but as you mentioned this can be wasteful if the user inputs a shorter length. Nevertheless this is usually the solution you'll find in real code - in practice, if memory isn't a huge concern, this is faster and simpler than trying to deal with other dynamic solutions that involve memory allocations.
Ask the user for the length of their name before they input it - then you can dynamically allocate an appropriately sized buffer using either char* name = malloc(input_length);, or char name[input_length]; in C99+. You could also do something like a flexible array member:
struct name {
size_t length;
char buffer[];
};
struct name* username = malloc(sizeof(*username) + username_length);
If you don't want to ask the user for the length of the username, you can do a chain of realloc calls after each new getchar, which will resize a dynamically allocated array - but this a terrible idea which you shouldn't even consider unless you're stressed over every byte of memory consumed in your program.
i am trying to convert a string (example: "hey there mister") into a double pointer that's pointing to every word in the sentence.
so: split_string->|pointer1|pointer2|pointer3| where pointer1->"hey", pointer2->"there" and pointer3->"mister".
char **split(char *s) {
char **nystreng = malloc(strlen(s));
char str[strlen(s)];
int i;
for(i = 0; i < strlen(s); i++){
str[i] = s[i];
}
char *temp;
temp = strtok(str, " ");
int teller = 0;
while(temp != NULL){
printf("%s\n", temp);
nystreng[teller] = temp;
temp = strtok(NULL, " ");
}
nystreng[teller++] = NULL;
//free(nystreng);
return nystreng;
}
My question is, why isnt this working?
Your code has multiple problems. Among them:
char **nystreng = malloc(strlen(s)); is just wrong. The amount of space you need is the size of a char * times the number pieces into which the string will be split plus one (for the NULL pointer terminator).
You fill *nystreng with pointers obtained from strtok() operating on local array str. Those pointers are valid only for the lifetime of str, which ends when the function returns.
You do not allocate space for a string terminator in str, and you do not write one, yet you pass it to strtok() as if it were a terminated string.
You do not increment teller inside your tokenization loop, so each token pointer overwrites the previous one.
You have an essential problem here in that you do not know before splitting the string how many pieces there will be. You could nevertheless get an upper bound on that by counting the number of delimiter characters and adding 1. You could then allocate space for that many char pointers plus one. Alternatively, you could build a linked list to handle the pieces as you tokenize, then allocate the result array only after you know how many pieces there are.
As for str, if you want to return pointers into it, as apparently you do, then it needs to be dynamically allocated, too. If your platform provides strdup() then you could just use
char *str = strdup(s);
Otherwise, you'll need to check the length, allocate enough space with malloc() (including space for the terminator), and copy the input string into the allocated space, presumably with strcpy(). Normally you would want to free the string afterward, but you must not do that if you are returning pointers into that space.
On the other hand, you might consider returning an array of strings that can be individually freed. For that, you must allocate each substring individually (strdup() would again be your friend if you have it), and in that event you would want to free the working space (or allow it to be cleaned up automatically if you use a VLA).
There are two things you need to do -
char str[strlen(s)]; //size should be equal to strlen(s)+1
Extra 1 for '\0'. Right now you pass str (not terminated with '\0') to strtok which causes undefined behaviour .
And second thing ,you also need allocate memory to each pointer of nystring and then use strcpy instead of pointing to temp(don't forget space for nul terminator).
I am trying to make an array with 10 pointers which will receive the input from scanf within a for loop. Then I want to call that list. However, I get a runtime error for memory:
#include <stdio.h>
int main(void) {
int i;
char * string[10];
//printf("%s", *string[0]);
for(i = 0; i<3; i++)
{
printf("what is string");
scanf("%s", string[i]);
printf("%s", string[i]);
}
return 0;
}
You need to think about the data structure with which you are attempting to store your strings.
char * string[10];
This declares an array of 10 elements of type "char *" or "pointer to a char".
Now think about what you are doing with this operation.
scanf("%s", string[i]);
You are using scanf to attempt to read user input. Does your function call make sense though?
The first parameter is your format string. "%s" makes sense because you want the user to provide you a string. Let's ignore the fact that you have not put any bounds on the length of that input for the moment.
Your second parameter needs to be be the variable where you want to store the input. You have provided "string[i]". Remember that in C, strings are just char arrays. However, your variable "string[i]" has the type "char *". This doesn't make sense. How can you store data of type "char array[]" in variable of type "char * array[]"? The types are different.
You need to create a buffer within which you can store your input string. Something like:
char buffer[256];
Then you call scanf like:
scanf("%s", buffer);
Keep in mind that this isn't the best idea because you don't know how many characters the user might enter. This will take the input character array and store them in a variable that correctly represents that data.
Note that this call would be safer:
fgets(buffer, sizeof(buffer)/sizeof(char), stdin);
fgets will read a fixed number of characters from the stream. In the above call I define that number by just taking the length of my buffer variable. I divide by sizeof(char) just to make sure that any strange representation issues where a char != 1 byte are taken care of. Also note that you will end up with the carraige return in your buffer (fgets will grab it).
Once you have your string in an appropriate variable, you can save it and get a pointer to a copy of that string with:
char * ptr = strdup(buffer);
This will create a new copy of the string stored in buffer and return a pointer to that new copy location. This pointer can then go into your array.
Note that you cannot just do:
string[i] = buffer
Because string[i] is just a pointer to the buffer variable...which will be overwritten during each iteration! You would end up with an array of pointers to the same variable, which would all end up giving you to the same string (the last input).
Put it all together in something like this:
char buffer[256];
printf("What is your string? ");
fgets(buffer, sizeof(buffer)/sizeof(char), stdin);
string[i] = strdup(buffer);
Because strdup() uses a malloc under the hood, you will need to free all your strings at the end of your program with:
int i = 0;
for(; i < sizeof(string)/sizeof(char *); i++) {
if(string[i]) free(string[i]);
}
You need to either allocate memeory dynamically or make your 2-d array:
char string[10][100];
You can do something like this to allocate memory -
for(i=0;i<10;i++){
string[i]=malloc(sizeof(char)*10);
}
So that your program does not give error.
But after this you have to free the memory allocated.
In the code below, I hope you can see that I have a char* variable and that I want to read in a string from a file. I then want to pass this string back from the function. I'm rather confused by pointers so I'm not too sure what I'm supposed to do really.
The purpose of this is to then pass the array to another function to be searched for a name.
Unfortunately the program crashes as a result and I've no idea why.
char* ObtainName(FILE *fp)
{
char* temp;
int i = 0;
temp = fgetc(fp);
while(temp != '\n')
{
temp = fgetc(fp);
i++;
}
printf("%s", temp);
return temp;
}
Any help would be vastly appreciated.
fgetc returns an int, not a char*. This int is a character from the stream, or EOF if you reach the end of the file.
You're implicitly casting the int to a char*, i.e., interpreting it as an address (turn your warnings on.) When you call printf it reads that address and continues to read a character at a time looking for the null terminator which ends the string, but that address is almost certainly invalid. This is undefined behavior.
I've taken some liberties with what you wanted to accomplish. Rather that deal with pointers, you can just use a fixed sized array as long as you can set a maximum length. I've also included several checks so that you don't run off the end of the buffer or the end of the file. Also important is to make sure that you have a null termination '\0' at the end of the string.
#define MAX_LEN 100
char* ObtainName(FILE *fp)
{
static char temp[MAX_LEN];
int i = 0;
while(i < MAX_LEN-1)
{
if (feof(fp))
{
break;
}
temp[i] = fgetc(fp);
if (temp[i] == '\n')
{
break;
}
i++;
}
temp[i] = '\0';
printf("%s", temp);
return temp;
}
So, there are several problems here:
You're not setting aside any storage for the string contents;
You're not storing the string contents correctly;
You're attempting to read memory that doesn't belong to you;
The way you're attempting to return the string is going to give you heartburn.
1. You're not setting aside storage for the string contents
The line
char *temp;
declares temp as a pointer to char; its value will be the address of a single character value. Since it's declared at local scope without the static keyword, its initial value will be indeterminate, and that value may not correspond to a valid memory address.
It does not set aside any storage for the string contents read from fp; that would have to be done as a separate step, which I'll get to below.
2. You're not storing the string contents correctly
The line
temp = fgetc(fp);
reads the next character from fp and assigns it to temp. First of all, this means you're only storing the last character read from the stream, not the whole string. Secondly, and more importantly, you're assigning the result of fgetc() (which returns a value of type int) to an object of type char * (which is treated as an address). You're basically saying "I want to treat the letter 'a' as an address into memory." This brings us to...
3. You're attempting to read memory that doesn't belong to you
In the line
printf("%s", temp);
you're attempting to print out the string beginning at the address stored in temp. Since the last thing you wrote to temp was most likely a character whose value is < 127, you're telling printf to start at a very low and most likely not accessible address, hence the crash.
4. The way you're attempting to return the string is guaranteed to give you heartburn
Since you've defined the function to return a char *, you're going to need to do one of the following:
Allocate memory dynamically to store the string contents, and then pass the responsibility of freeing that memory on to the function calling this one;
Declare an array with the static keyword so that the array doesn't "go away" after the function exits; however, this approach has serious drawbacks;
Change the function definition;
Allocate memory dynamically
You could use dynamic memory allocation routines to set aside a region of storage for the string contents, like so:
char *temp = malloc( MAX_STRING_LENGTH * sizeof *temp );
or
char *temp = calloc( MAX_STRING_LENGTH, sizeof *temp );
and then return temp as you've written.
Both malloc and calloc set aside the number of bytes you specify; calloc will initialize all those bytes to 0, which takes a little more time, but can save your bacon, especially when dealing with text.
The problem is that somebody has to deallocate this memory when its no longer needed; since you return the pointer, whoever calls this function now has the responsibility to call free() when it's done with that string, something like:
void Caller( FILE *fp )
{
...
char *name = ObtainName( fo );
...
free( name );
...
}
This spreads the responsibility for memory management around the program, increasing the chances that somebody will forget to release that memory, leading to memory leaks. Ideally, you'd like to have the same function that allocates the memory free it.
Use a static array
You could declare temp as an array of char and use the static keyword:
static char temp[MAX_STRING_SIZE];
This will set aside MAX_STRING_SIZE characters in the array when the program starts up, and it will be preserved between calls to ObtainName. No need to call free when you're done.
The problem with this approach is that by creating a static buffer, the code is not re-entrant; if ObtainName called another function which in turn called ObtainName again, that new call will clobber whatever was in the buffer before.
Why not just declare temp as
char temp[MAX_STRING_SIZE];
without the static keyword? The problem is that when ObtainName exits, the temp array ceases to exist (or rather, the memory it was using is available for someone else to use). That pointer you return is no longer valid, and the contents of the array may be overwritten before you can access it again.
Change the function definition
Ideally, you'd like for ObtainName to not have to worry about the memory it has to write to. The best way to achieve that is for the caller to pass target buffer as a parameter, along with the buffer's size:
int ObtainName( FILE *fp, char *buffer, size_t bufferSize )
{
...
}
This way, ObtainName writes data into the location that the caller specifies (useful if you want to obtain multiple names for different purposes). The function will return an integer value, which can be a simple success or failure, or an error code indicating why the function failed, etc.
Note that if you're reading text, you don't have to read character by character; you can use functions like fgets() or fscanf() to read an entire string at a time.
Use fscanf if you want to read whitespace-delimited strings (i.e., if the input file contains "This is a test", fscanf( fp, "%s", temp); will only read "This"). If you want to read an entire line (delimited by a newline character), use fgets().
Assuming you want to read an individual string at a time, you'd use something like the following (assumes C99):
#define FMT_SIZE 20
...
int ObtainName( FILE *fp, char *buffer, size_t bufsize )
{
int result = 1; // assume success
int scanfResult = 0;
char fmt[FMT_SIZE];
sprintf( fmt, "%%%zus", bufsize - 1 );
scanfResult = fscanf( fp, fmt, buffer );
if ( scanfResult == EOF )
{
// hit end-of-file before reading any text
result = 0;
}
else if ( scanfResult == 0 )
{
// did not read anything from input stream
result = 0;
}
else
{
result = 1;
}
return result;
}
So what's this noise
char fmt[FMT_SIZE];
sprintf( fmt, "%%%zus", bufsize - 1 );
about? There is a very nasty security hole in fscanf() when you use the %s or %[ conversion specifiers without a maximum length specifier. The %s conversion specifier tells fscanf to read characters until it sees a whitespace character; if there are more non-whitespace characters in the stream than the buffer is sized to hold, fscanf will store those extra characters past the end of the buffer, clobbering whatever memory is following it. This is a common malware exploit. So we want to specify a maximum length for the input; for example, %20s says to read no more than 20 characters from the stream and store them to the buffer.
Unfortunately, since the buffer length is passed in as an argument, we can't write something like %20s, and fscanf doesn't give us a way to specify the length as an argument the way fprintf does. So we have to create a separate format string, which we store in fmt. If the input buffer length is 10, then the format string will be %10s. If the input buffer length is 1000, then the format string will be %1000s.
The following code expands on that in your question, and returns the string in allocated storage:
char* ObtainName(FILE *fp)
{
int temp;
int i = 1;
char *string = malloc(i);
if(NULL == string)
{
fprintf(stderr, "malloc() failed\n");
goto CLEANUP;
}
*string = '\0';
temp = fgetc(fp);
while(temp != '\n')
{
char *newMem;
++i;
newMem=realloc(string, i);
if(NULL==newMem)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
string=newMem;
string[i-1] = temp;
string[i] = '\0';
temp = fgetc(fp);
}
CLEANUP:
printf("%s", string);
return(string);
}
Take care to 'free()' the string returned by this function, or a memory leak will occur.
I have a text file called "graphics" which contains the words "deoxyribonucleic acid".
When I run this code it works and it returns the first character. "d"
int main(){
FILE *fileptr;
fileptr = fopen("graphics.txt", "r");
char name;
if(fileptr != NULL){ printf("hey \n"); }
else{ printf("Error"); }
fscanf( fileptr, "%c", &name);
printf("%c\n", name);
fclose( fileptr );
return 0;
}
When I am using the fscanf function the parameters I am sending are the name of the FILE object, the type of data the function will read, and the name of the object it is going to store said data, correct? Also, why is it that I have to put an & in front of name in fscanf but not in printf?
Now, I want to have the program read the file and grab the first word and store it in name.
I understand that this will have to be a string (An array of characters).
So what I did was this:
I made name into an array of characters that can store 20 elements.
char name[20];
And changed the parameters in fscanf and printf to this, respectively:
fscanf( fileptr, "%s", name);
printf("%s\n", name);
Doing so produces no errors from the compiler but the program crashes and I don't understand why. I am letting fscanf know that I want it to read a string and I am also letting printf know that it will output a string. Where did I go wrong? How would I accomplish said task?
This is a very common problem. fscanf reads data and copies it into a location you provide; so first of all, you need the & because you provide the address of the variable (not the value) - that way fscanf knows where to copy TO.
But you really want to use functions that copy "only as many characters as I have space". This is for example fgets(), which includes a "max byte count" parameter:
char * fgets ( char * str, int num, FILE * stream );
Now, if you know that you only allocated 20 bytes to str, you can prevent reading more than 20 bytes and overwriting other memory.
Very important concept!
A couple of other points. A variable declaration like
char myString[20];
results in myString being a pointer to 20 bytes of memory where you can put a string (remember to leave space for the terminating '\0'!). So you can usemyStringas thechar *argument infscanforfgets`. But when you try to read a single character, and that characters was declared as
char myChar;
You must create the pointer to the memory location "manually", which is why you end up with &myChar.
Note - if you want to read up to white space, fscanf is the better function to use; but it will be a problem if you don't make sure you have the right amount of space allocated. As was suggested in a comment, you could do something like this:
char myBuffer[20];
int count = fscanf(fileptr, "%19s ", myBuffer);
if(count != 1) {
printf("failed to read a string - maybe the name is too long?\n");
}
Here you are using the return value of fscanf (the number of arguments correctly converted). You are expecting to convert exactly one; if that doesn't work, it will print the message (and obviously you will want to do more than print a messageā¦)
Not answer of your question but;
for more efficient memory usage use malloc instead of a static declaration.
char *myName // declara as pointer
myName = malloc(20) // same as char[20] above on your example, but it is dynamic allocation
... // do your stuff
free(myName) // lastly free up your allocated memory for myName