I've got a linked list of nodes that contain a string of characters. The program reads characters from stdin until it reaches a new line and once it has it puts this string of characters into the a new node of the list.
I've done some debugging of the different steps involved in the program and can see the list of nodes being created correctly.
However, the printf statement doesn't seem to do anything if I'm stepping through the code. If I don't step through and just run the code through I get:
Cannot access memory at address 0x2e666564
Cannot access memory at address 0x2e666564
Cannot access memory at address 0x2e666564
My source code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node {
char *string;
struct Node *next;
} List;
List *addNode(List *currentList, char *character)
{
List *tempList = calloc(1, sizeof(List));
tempList->string = strdup(character);
tempList->next = currentList;
return tempList;
}
void printList(List *currentList)
{
while (currentList != NULL)
{
printf("%s", currentList->string);
currentList = currentList->next;
}
}
int main ()
{
char currentCharacter;
char *currentString;
List *mainList = NULL;
do
{
currentCharacter = getchar();
if (currentCharacter == '\n')
{
mainList = addNode(mainList, currentString);
currentString[0] = '\0';
} else {
strcat(currentString, ¤tCharacter);
}
} while (currentCharacter != '.');
mainList = addNode(mainList, currentString);
printList(mainList);
return 0;
}
You're calling strcat on an invalid pointer.
Something like this would work:
char currentString[128];
currentString[0] = '\0';
currentCharacter isn't null-terminated and so strcat won't work. Use strncat instead.
Several problems here.
The main problem is that you have not allocated space for your currentString. strcat requires that there be space in the destination array (currentString).
Also problematic: when the user enters '\n', you haven't null terminated the string you are appending, so strdup will not quite work.
Your main function never initializes currentString to point to allocated memory, so your call to strcat just starts adding characters to whatever '\0'-terminated string currentString happens to be pointing to.
You don't need strcat. You do need to assign currentString at some point. Replace:
#define BUFFER_SIZE 128
/*
shouldn't need more than 128 characters for a line of console
but you can expand it later if you want
*/
int main() {
List *mainList = NULL;
char *buffer = malloc(sizeof(char)*BUFFER_SIZE);
int currentIndex = 0;
char currentCharacter;
do {
currentCharacter = getChar();
if (currentCharacter == '\n' || currentIndex == (BUFFER_SIZE-1))
buffer[currentIndex] = 0; //the null byte needs to be added before strdup is called
mainList = addNode(mainList, buffer);
currentIndex = 0;
} else {
buffer[currentIndex++] = currentCharacter;
}
} while (currentCharacter != '.');
mainList = addNode(mainList, buffer);
return 0;
}
Related
This question already has answers here:
Trying to remove all numbers from a string in C
(4 answers)
Closed 1 year ago.
I don't have any idea to remove digits from a string. My first ides was to use a for-loop to go through the string. With the function is_digit you can check if it's a digit. But when I identify that what should I do now or somebody have a better way to remove digits.
#include "base.h"
bool is_digit(char c) {
return c >= ’0’ && c <= ’9’;
}
char *remove_digits(char *s) {
// to do
}
void test(void) {
test_equal_s(remove_digits(""), "");
test_equal_s(remove_digits("x"), "x");
test_equal_s(remove_digits("11"), "");
test_equal_s(remove_digits("11x"), "x");
test_equal_s(remove_digits("x11"), "x");
test_equal_s(remove_digits("x11x"), "xx");
test_equal_s(remove_digits("1a2b3c4"), "abc");
}
int main(void) {
test();
return 0;
}
If you want do use your own algorithm you can use do something like this:
#include <string.h> // for strlen
char *remove_digits(char *s)
{
char *new_s = malloc(strlen(s) + 1); // allocate memory for the new string
if (new_s == NULL)
{
return new_s; // return NULL if malloc fails
}
char *new_s_begin = new_s; // pointer to keep at the beggining of the new string
while (*s) // while the string doesn't reach the null byte
{
if (!is_digit((unsigned char)*s)) // if the character it is not a digit...
{
*new_s++ = *s; // copy it to the new string
}
s++; // next character
}
*new_s = 0; // null terminate string
return new_s_begin; // return a pointer to the beginning of the new string
}
Since the string you'll have to pass might be different from the original, first thing you make a copy.
char* remove_digits(char* s) {
char *copy;
// reserve space for string and its terminating zero
copy = malloc(strlen(s)+1);
// Never trust malloc to return correctly
if (NULL == copy) {
return NULL;
}
// Then the idea is to copy only the non-digits.
char *sp; // Source pointer
char *dp = copy; // Destination pointer
for (sp = s; *sp; sp++) {
if (!is_digit(*sp)) {
*(dp++) = *sp;
}
}
// At the end, "copy" is not yet a valid C string because it
// need to be zero- terminated, so:
*(dp) = 0x0;
return copy;
}
with following code I can store one string only.
Main problem is how to store several. If i want to enter another string after the first one it wont do it.
I didnt write it in code but when I type("KRAJ") it should get out of while loop.
typedef struct{
char Objekat[20+1];
char Mjesto[20+1];
char velicina [20];
int cijena;
char kn[3];
char stanje[20];
}Apartmani;
int main()
{
Apartmani *apartmani=(Apartmani*)malloc(sizeof(Apartmani)*50);
while(scanf("%[^,\n],%[^,],%[^,],%d%[^,],%[^\n]", &apartmani[i].Objekat,&apartmani[i].Mjesto,&apartmani[i].velicina,
&apartmani[i].cijena,&apartmani[i].kn, &apartmani[i].stanje )==6)
{
i++;
}
for(p=0;p<i;p++)
{
printf("%s %s %s %d %s %s",apartmani[p].Objekat,apartmani[p].Mjesto,apartmani[p].velicina,apartmani[p].cijena,
apartmani[p].kn, apartmani[p].stanje);
}
}
For example:
string 1: Apartman, Novalja, 100.00 m2, 750000kn, dobro ocuvano.
string 2: Kuca, Ivanbregovia, 20m2, Imtoski, 21252RH, vrijednost-neprocjenjiva.
You should use fgets() plus sscanf().
You should not cast malloc[Do I cast the result of malloc?][1]. Remember to check the return value of malloc, since it can be failed.
change the line of allocating apartmani to:
Apartmani *apartmani= malloc(sizeof(Apartmani)*50);
if(!apartmani) {return -1;}
Do not use & for the input of string.
Check the value of i because its value is limited to 50.
Your code is missing the declaration of i (should be: int i = 0), and the declaration of p also.
Your while loop can be as below:
int i = 0;
char line[100];
while(i < 50 && fgets(line,sizeof(line),stdin))
{
line[strcspn (line, "\n" )] = '\0'; // trip the enter character at the end of line.
int err = sscanf(line,"%20[^,],%20[^,],%19[^,],%d,%2[^,],%19[^\n]", apartmani[i].Objekat,apartmani[i].Mjesto,apartmani[i].velicina,&apartmani[i].cijena,
apartmani[i].kn, apartmani[i].stanje);
if(err != 6)
break;
i++;
}
If I understand you correctly, you want to store several 'Apartmani' structures.
In this case, you have 2 main possibilites :
Using array of structures (Fastest to write but less efficient)
Use linked-list (More efficient but more complex to use)
Examples
1: Using array of structures
#define MAX_APARTMANI 50
int main(void) {
int i = 0;
/* Create Apartmani array */
Apartmani *apartmani_tab[MAX_APARTMANI];
do {
/* loop by using malloc on a single element */
apartmani_tab[i] = (Apartmani *) malloc(sizeof(Apartmani));
/* While check using scanf */
} while (scanf("%[^,\n],%[^,],%[^,],%d%[^,],%[^\n]", apartmani_tab[i]->Objekat, apartmani_tab[i]->Mjesto, apartmani_tab[i]->velicina,
apartmani_tab[i]->cijena, apartmani_tab[i]->kn, apartmani_tab[i]->stanje) == 6 && ++i < MAX_APARTMANI)
/* good pratice: don't forget to free memory ! */
while (--i > 0) {
free(apartmani_tab[i]);
}
return (0);
}
2: Using linked-list
typedef struct Apartmani {
char Objekat[20+1];
char Mjesto[20+1];
char velicina [20];
int cijena;
char kn[3];
char stanje[20];
struct Apartmani *next;/* add pointer to next item in the list */
} Apartmani_t;
Apartmani_t *new_item(void) {
Apartmani_t *new_element = NULL;
new_element = (Apartmani_t *) malloc(sizeof(Apartmani));
if (!new_element)
return (NULL);
memset(new_element, 0, sizeof(*new_element));
new_element->next = NULL;
return (new_element);
}
int main(void) {
/* Initialize Apartmani list*/
Apartmani *apartmani_list = NULL, *current = NULL;
do {
if (!apartmani_list) { /* if empty list */
apartmani_list = new_item(); /* add first item */
if (!apartmani_list) /* prevent malloc errors */
break;
current = apartmani_list; /* link current pointer to list */
} else {
current->next = new_item();
if (!current->next) /* if malloc fails */
break;
current = current->next; /* update current pointer */
}
} while (scanf("%[^,\n],%[^,],%[^,],%d%[^,],%[^\n]", current->Objekat, current->Mjesto, current->velicina, current->cijena, current->kn, current->stanje) == 6) /* While check using scanf */
/* good pratice: don't forget to free memory ! */
while (apartmani_list) {
current = apartmani_list->next;
free(apartmani_list);
apartmani_list = current;
}
}
NB: I have not tried this code but the final version is probably very close to that.
So I'm trying to combine two strings together but I'm getting str is a read only value on the last line of the second while loop. Is their anyone can I do this without changing the function header?
Also String is a struct I created that has a *char called str.
String * String_Combine(String * tar, const String * src) {
//end of string
while (* tar->str != '\0') {
tar->str++;
}
//int i = 0;
//copy string
while (*source->str != '\0') {
*tar->str = *src->str;
*tar->str++;
*src->str++;
// i++;
}
return tar;
}
Copy the pointer before modifying it. I guess modifying tar->str may also be harmful because it will destroy the information that where the string starts.
String * String_Combine(String * tar, const String * src) {
char * tar_str = tar->str, * src_str = src->str;
//end of string
while (* tar_str != '\0') {
tar_str++;
}
//int i = 0;
//copy string
while (*src_str != '\0') { /* assuming that "source" is a typo and it should be "src" */
*tar_str = *src_str; /* with hope that there is enough writable buffer allocated */
tar_str++;
src_str++;
// i++;
}
//terminate string
*tar_str = '\0';
return tar;
}
Two things:
Make sure you allocate enough memory for tar->strto hold both tar->str and src->str
Store a pointer tar/src->str locally and iterate thought them so you wouldn't loose the pointer to original str;
I wrote a test case for you to understand it easily ;)
#include <iostream>
#include <string.h>
struct String {
char* str;
};
using namespace std;
String * String_Combine(String * tar, const String * src) {
// take a local copy of strs
char* tar_str = tar->str;
char* src_str = src->str;
//end of string
while (* tar_str != '\0') {
tar_str++;
}
//int i = 0;
//copy src string to tar string
while (*src_str != '\0') {
*tar_str = *src_str;
*tar_str++;
*src_str++;
// i++;
}
return tar;
}
int main()
{
String* src = (String*) malloc(sizeof(String));
src->str = new char[20];
strcpy(src->str, "World!");
String* tar = (String*) malloc(sizeof(String));
tar->str = new char[20];
strcpy(tar->str, "Hello ");
String* result = String_Combine(tar,src);
cout << result->str << endl;
return 0;
}
I want to pass the glist pointer to the function so that I can get the changed value in main function.
I have code as:
#include <stdio.h>
#include <string.h>
#include <glib.h>
char *col_trim_whitespace(char *str)
{
char *end;
// Trim leading space
while(isspace(*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) end--;
// Write new null terminator
*(end+1) = 0;
return str;
}
void line_parser(char *str,GSList* list1)
{
GSList* list = NULL;
char *token, *remstr=NULL ;
token = strtok_r(str,"\n",&remstr);
while(token != NULL)
{
if(token[0] == ' ')
{
token = col_trim_whitespace(token);
if(strcmp(token,"")==0)
{
token = strtok_r(NULL, "\n", &remstr);
continue;
}
}
list1 = g_slist_append(list1, token);
token = strtok_r(NULL,"\n",&remstr);
}
}
int main()
{
int *av,i,j,length;
i=0;
char str[] = " this name of \n the pet is the ffffffffffffffffffffffffffffff\n is \n the \n test\n program";
//GSList *list1 = line_parser(str);
GSList *list1 = NULL;
line_parser(str,list1 );
// printf("The list is now %d items long\n", g_slist_length(list));
length = g_slist_length(list1);
// printf("length=%d", length);
for(j=0;j<length;j++)
{
printf("string = %s\n",(char *)g_slist_nth(list1,j)->data);
}
g_slist_free(list1);
return 0;
}
here I have a list name list1 in the main function, then I passed list1 as an argument to the lineparser() function where the list is changed appending some values. and I want to return the value to the main() function but without using a return statement as I have used the pointer reference while passing argument. but the value is not returned to the main() function. How can I achieve this?
Seems you want to pass the address of the list pointer, and then have the parser update that variable:
void line_parser(char *str,GSList **list1)
{
...
*list1= list;
}
and in main:
main()
{
GSList *list1 = NULL;
line_parser(str, &list1);
...
}
As it appears, g_slist_append() returns the new start pointer of the list. So, essentially, you're trying to change the list1 value from the line_parser() and expecting that value to be reflected back into main().
Well, that is not possible in the current form. C uses pass-by-value for function parameter passing, so all the arguments passed to a function are a separate local copy to the called function, while considering as parameters to the called function.
If you want the change to list1 to be reflected to main(), you need to use a pointer-to-pointer as the input parameter.
Something along the line of
void line_parser(char *str,GSList** list1) { //and other usage
and
line_parser(str,&list1 );
should solve your problem.
There are many ways to do this. One way:
This line GSList *list1 = NULL; creates a pointer, not the GSList structure. You might want to allocate memory for the list using malloc and cast it to a GSList. For example, GSList list1 = (GSList) malloc(sizeof(GSList));
Then in your function line_parser we will need to pass a pointer line_parser(str, &list1)
Remember to free(list1)
Another way would be to create the GSList on the stack. Regardless, we still pass a pointer to line_parser, as mentioned above.
I'm trying to make a quick function that gets a word/argument in a string by its number:
char* arg(char* S, int Num) {
char* Return = "";
int Spaces = 0;
int i = 0;
for (i; i<strlen(S); i++) {
if (S[i] == ' ') {
Spaces++;
}
else if (Spaces == Num) {
//Want to append S[i] to Return here.
}
else if (Spaces > Num) {
return Return;
}
}
printf("%s-\n", Return);
return Return;
}
I can't find a way to put the characters into Return. I have found lots of posts that suggest strcat() or tricks with pointers, but every one segfaults. I've also seen people saying that malloc() should be used, but I'm not sure of how I'd used it in a loop like this.
I will not claim to understand what it is that you're trying to do, but your code has two problems:
You're assigning a read-only string to Return; that string will be in your
binary's data section, which is read-only, and if you try to modify it you will get a segfault.
Your for loop is O(n^2), because strlen() is O(n)
There are several different ways of solving the "how to return a string" problem. You can, for example:
Use malloc() / calloc() to allocate a new string, as has been suggested
Use asprintf(), which is similar but gives you formatting if you need
Pass an output string (and its maximum size) as a parameter to the function
The first two require the calling function to free() the returned value. The third allows the caller to decide how to allocate the string (stack or heap), but requires some sort of contract about the minumum size needed for the output string.
In your code, when the function returns, then Return will be gone as well, so this behavior is undefined. It might work, but you should never rely on it.
Typically in C, you'd want to pass the "return" string as an argument instead, so that you don't have to free it all the time. Both require a local variable on the caller's side, but malloc'ing it will require an additional call to free the allocated memory and is also more expensive than simply passing a pointer to a local variable.
As for appending to the string, just use array notation (keep track of the current char/index) and don't forget to add a null character at the end.
Example:
int arg(char* ptr, char* S, int Num) {
int i, Spaces = 0, cur = 0;
for (i=0; i<strlen(S); i++) {
if (S[i] == ' ') {
Spaces++;
}
else if (Spaces == Num) {
ptr[cur++] = S[i]; // append char
}
else if (Spaces > Num) {
ptr[cur] = '\0'; // insert null char
return 0; // returns 0 on success
}
}
ptr[cur] = '\0'; // insert null char
return (cur > 0 ? 0 : -1); // returns 0 on success, -1 on error
}
Then invoke it like so:
char myArg[50];
if (arg(myArg, "this is an example", 3) == 0) {
printf("arg is %s\n", myArg);
} else {
// arg not found
}
Just make sure you don't overflow ptr (e.g.: by passing its size and adding a check in the function).
There are numbers of ways you could improve your code, but let's just start by making it meet the standard. ;-)
P.S.: Don't malloc unless you need to. And in that case you don't.
char * Return; //by the way horrible name for a variable.
Return = malloc(<some size>);
......
......
*(Return + index) = *(S+i);
You can't assign anything to a string literal such as "".
You may want to use your loop to determine the offsets of the start of the word in your string that you're looking for. Then find its length by continuing through the string until you encounter the end or another space. Then, you can malloc an array of chars with size equal to the size of the offset+1 (For the null terminator.) Finally, copy the substring into this new buffer and return it.
Also, as mentioned above, you may want to remove the strlen call from the loop - most compilers will optimize it out but it is indeed a linear operation for every character in the array, making the loop O(n**2).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *arg(const char *S, unsigned int Num) {
char *Return = "";
const char *top, *p;
unsigned int Spaces = 0;
int i = 0;
Return=(char*)malloc(sizeof(char));
*Return = '\0';
if(S == NULL || *S=='\0') return Return;
p=top=S;
while(Spaces != Num){
if(NULL!=(p=strchr(top, ' '))){
++Spaces;
top=++p;
} else {
break;
}
}
if(Spaces < Num) return Return;
if(NULL!=(p=strchr(top, ' '))){
int len = p - top;
Return=(char*)realloc(Return, sizeof(char)*(len+1));
strncpy(Return, top, len);
Return[len]='\0';
} else {
free(Return);
Return=strdup(top);
}
//printf("%s-\n", Return);
return Return;
}
int main(){
char *word;
word=arg("make a quick function", 2);//quick
printf("\"%s\"\n", word);
free(word);
return 0;
}