I am trying to count how many times a . appear in a single string passed in by the command line.
calling myprog "this...is a test."
returns The count is 0?
What am I doing wrong here?
Note: I know this code may look odd but is for education purposes
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int len = strlen(argv[1]);
char *d = malloc (strlen(argv[1])+1);
strcpy(d,argv[1]);
char *p=d;
int count;
count=0;
while(*p){
if (*p ==','){
count++;
}
*p++;
}
printf("The count is: %d\n", count);
return 0;
}
You are counting the number of commas, not of periods. To count periods change the if statement to:
if (*p =='.'){
count++;
}
This code has quite a few...oddities. It's a little hard to guess which are intentional and which aren't, so let's go through it line by line and see what's there.
int len = strlen(argv[1]);
char *d = malloc (strlen(argv[1])+1);
strcpy(d,argv[1]);
It appears you probably intended to use len at some point, but as it stands right now, you get len, then re-compute the same value to use it. Presumably you intended something more like:
size_t len = strlen(argv[1]);
char *d = malloc(len+1);
strcpy(d, argv[1]);
I'd note, however, that there's really no reason to do any of this. Since you're just trying to examine the contents, you might as well just use argv[1] directly (or create another pointer to the same place and use that).
char *p=d;
This creates another pointer to the same location as d. You didn't really need d to start with, and you don't really need this either, but it's fairly harmless.
int count;
count=0;
I'd (strongly) prefer to see count initialized rather than left uninitialized, then assigned a value afterwards. Since there's no possibility of its being negative, I'd probably also make it an unsigned type: size_t count = 0;
while(*p){
if (*p ==','){
count++;
}
*p++;
}
As others have already pointed out, you're comparing to the wrong value here. I'd also note, however, that when you have an initialization, a test, and an "increment" operation of some sort, you're almost certainly better off using a for loop instead of a while loop.
In addition, you have the increment part a bit wrong here. You really only want p++, not *p++.
for (char *p=d; *p; ++p)
if (*p == '.')
++count;
When we get down to it, a slightly modified version of that loop is pretty much all we really need for the whole task though:
char const *p;
for (p = argv[1]; *p; ++p)
if (*p == '.')
++count;
change
if (*p ==',')
to
if (*p =='.')
to count ..
I believe there is just a typo. Replace ',' with '.' and it will return "4", I've just tested it.
Related
I started learning C and I had this exercise from the book "Prentice Hall - The C Programming Language".
Chapter 5 Exercise 3:
Write a pointer version of the fuction strcat that we showed in Chapter 2. strcat(s, t) copies the string t to the end of s.
I did the exercise but the first method that came up to my mind was:
void stringcat(char *s, char *t){
int i,j;
i = j = 0;
while(*(s+i) != '\0'){
printf("%d", i);
i++;
}
while ( (*(t+j)) != '\0'){
*(s+i) = *(t+j);
i++;
j++;
}
}
In main I had:
int main(){
char s[] = "Hola";
char t[] = "lala";
stringcat(s,t);
printf("%s\n", s);
}
At first sight I thought it was right but the actual output was Holalalaa.
Of course it was not the output that I expected, but then I coded this:
void stringcat(char *s, char *t){
int i,j;
i = j = 0;
while(*(s+i) != '\0'){
printf("%d", i);
i++;
}
while((*(s+i) = *(t+j)) != '\0'){
i++;
j++;
}
}
And the output was right.
But then I was thinking a lot about the first code because it's very similar to the second one but why the first output was wrong?. Is it something related with the while statement? or something with pointers?. I found it really hard to understand because you can't see what's happening in the array.
Thanks a lot.
Your code has more than the one problem that you found, but let's start with it.
Actually you are asking why
/* ... */
while ((*(t+j)) != '\0') {
*(s+i) = *(t+j);
/* ... */
works differently than
/* ... */
while ((*(s+i) = *(t+j)) != '\0') {
/* ... */
I hope you see it already, now that both cases stand side by side, actually vertically ;-). In the first case the value of t[j] is compared before it is copied to s[i]. In the second case the comparison is done after the copy. That's why the second case copies the terminating '\0' to the target string, and the first case does not.
The output you get works accidentally, it is Undefined Behavior, since you are writing beyond the border of the target array. Fortunately for you, both strings are laying in sequence in the memory, and you are overwriting the source string with its own characters.
Because your first case does not copy the '\0', the final printf() outputs more characters until a '\0' is encountered. By chance this is the last 'a'.
As others commented, the target string has not enough space for the concatenated string. Provide some more space like this:
char s[10] = "Hola"; /* 10 is enough for both strings and the terminating '\0'. */
However, if you had done this already, the error would have not been revealed, because the last 6 characters of s are initialized with '\0'. Not copying the terminating '\0' makes no difference. You can see this if you use
char s[10] = "Hola\0xxxx";
I don't think that your solution is the expected one. Instead of s[i] you are using *(s + i), which is essentially the same, accessing an array. Consider changing s (and in the course, t) in the function and use just *s.
Side note: The printf() in the function is most probably a leftover from debugging. But I'm sure you know.
I am learning C and I came across a problem while manipulating strings.
In a problem I was solving I was supposed to write a function to take a string and a character and delete all occurrences of the given character, and then I had to return the modified string.
The function I wrote is this:
char *strdelc3(char *s, char ch){
for(int i=0,j=0; i!=strlen(s)+1; ++i)
if(s[i]!=ch){
s[j]=s[i];
++j;
}
return s;
}
And when I pass a string and a character as arguments:
main(){
char s[20]="mary";
puts(strdelc3(s,'r'));
}
The output is: Segmentation fault(core dumped),
which by my research means I am accessing memory that does not belong to me.
The solutions had this code:
char *strdelc4(char *s, char ch){ /*Correct*/
int i,j;
for(i=0, j=0; s[i]!='\0'; ++i)
if(s[i]!=ch){
s[j]=s[i];
++j;
}
s[j]='\0';
return s;
}
Which is basically equal to mine, however this piece works fine!
Since the two codes are so similar I don't see anything wrong with mine...
I have already studied both but I don't see what is the problem with mine... Could someone help?
The problem is in your loop conditional:
i!=strlen(s)+1
You're attempting to use strlen(s)+1 here to avoid having to add the null byte. But in doing so, strlen(s) changes once you move the terminating null byte.
On the first 4 iterations through the loop, strlen(s) is 4. On the next iteration, i is 4 and strlen(s)+1 is 5 so you enter the loop again. You then move the null byte. Now on the following iteration, strlen(s) is 3 and i is 5. The conditional is still true so you keep going, walking off the end of the string. This invokes undefined behavior which in this case causes a crash.
The second piece of code addresses this issue by explicitly looking for the null byte based on the index of i and appending a null byte to the resulting string after the loop.
An even simpler version of the code would use the do - while loop instead of for():
char *strdelc5idx(char *s, char ch){
int i=0, j=0;
do {
if (s[i] != ch)
s[j++] = s[i];
} while (s[i++] != 0);
return s;
}
This will copy the string-terminating NUL character before testing it, so you needn't have a separate instruction for it. However, that requires deferring the i++ incrementation so that the loop condition at the end of an iteration tests the same character which was copied in the iteration. As a result the i++ and j++ do no longer appear together, which may make this code less legible at a first glance.
An equivalent pointer version:
char *strdelc5ptr(char *s, char ch){
char *d = s, *f = s;
do {
if (*f != ch)
*d++ = *f;
} while (*f++);
return s;
}
Consider the two functions below. Both functions compute the number of times a character appears in a string with a specified length.
int str_get_num_occurrences1(char * str, char c, unsigned int len){
if (!len)
len = strlen(str);
int res = 0;
int n = len;
for ( ; n--; )
if (str[n] == c)
res++;
return res;
}
int str_get_num_occurrences2(char * str, char c, unsigned int len){
int res = 0;
if (!len)
len = strlen(str);
for ( ; len--; )
if (str[len] == c)
res++;
return res;
}
Obviously, the two functions do the same thing. Besides the fact that the first function is a little bit more readable than the second, is the second function more efficient since it avoids a local variable? I'm sure that these particular functions are really too simple to measure a true difference. I'm asking in more of a general or theoretical way.
Are there reasons why a user should avoid using input parameters as temporary storage (besides readability)? I'm not asking about pointers, where the input could be changed by the function. Does the compiler interpret the two functions differently which could cause function one to be preferred?
I searched through the questions, and I did find some related questions but none that I could find discussed the efficiency.
TL;DR
Write the code you find easiest to read/write/maintain. The difference between your functions will probably disappear when you compile with optimizations.
You might want to think about a couple of things that you can do to write a more flexible function, or at least: code that is easier to read. This answer will focus more on coding style, than the question Which is best, X or Y, because the answer will almost always be That depends on Z
Given that you're allowing the call to pass a 0 value for the string length, you could just write something like this:
int get_char_count(const char *str, char c)
{
int count = 0;
while(*str++) {
if (*str == c) {
++count;
}
}
return count;
}
That, to me, looks like the least amount of code, it's easy to read, and easy to maintain.
The drawbacks are:
Strings with '\0' characters in the middle (ie char[][]) can't be processed in full in a single call using this approach
Not possible to get the char count in a part of the string.
Strings containing '\0' chars can't be processed in full
If you want to support those use cases, you'll have to add a length argument. But even then, I'd just add it to the function, and not call strlen:
int get_char_count(const char *str, char c, unsigned int len)
{
int count = 0;
if (!len) {
while(*str++) {
if (*str == c) {
++count;
}
}
return count; // return early
}
//len is given
while (len--) {
if (str[len] == c) {
++count;
}
}
return count;
}
Now that I'm able to specify how many characters to iterate over, rather than to return on '\0', I can use this function, for example, to count how many occurrences of a given character are in an array of strings:
Example: count in char[][]
Example: cont in part of a string
Example: string with nul-chars
The first case (char [][]) works because of how the arrays are stored in memory: An array is a contiguous block of memory, and all values are stored in succession. If you know the total size of said block, you can use a char[][] as though it is one big string. The result being: only 1 function call is needed to count a character in all elements of the array.
The last case is pretty much the same thing, because the string in the example is actually how an array of strings is stored.
The second example (counting in partial string) is self-evident: rather than specifying the length of the full string, you can specify the number of characters you want to check...
The same approach can be used for strings lacking a terminating nul character
Because this is a fairly trivial function to implement, it's common to see most of the brackets being omitted:
while (*str++)
if (*str == c)
++count;
//or even
while(len--) count += str[len] == c;
The last version is technically valid, but it's not as easy to read. Omitting brackets for one-line if's and simple loops is fairly common, but has been the cause of bugs, like the goto fail bug from a few years back.
One last style-related thing:
When using the pointer to iterate over the string like I did in the first snippet, some will tell you that the best thing to do is to create a local pointer to increment:
int get_char_count(const char *str, char c)
{
int count = 0;
const char *local = str;
while(*local++) {
if (*local == c) {
++count;
}
}
return count;
}
The obvious advantage here being that you're not losing the original position/pointer that was passed in. If you later add something to the function, you can always reassign, or assign a new pointer based off str.
I was told to write a program containing a concatenate function. This program should collect the input strings using fgets (&s1[0], len1+1, stdin)
and then add the two to each other to produce a final product.
My problem falls in that the program compiles but it doesn't display anything on the screen whatsoever, here's what I've got. I couldn't see how I could get it solved without this method of approach.
//function to terminate the program incase reach of 0
int str_len (char s[])
{
int i=0;
while (s[i]= NULL)
++i;
return i+1;
}
char string_cat (char*s1, char*s2)
{
//ADDING THE TWO STRINGS
int str_len(char s[])
char *s1 [80]= {'\0'};
char *s2 [40]= {'\0'};
int len1=str_len(s1);
int len2=str_len(s2);
if (int x=0; len1+len2<80; \0;
return;
}
int main ()
{
char string_cat(char*s1,char*s2)
int str_len(char s[])
//RECIVING THE STRINGS TO ADD
char s1 [80];
char s2 [40];
int i=0;
for (i; i !=0; ++i)
{
printf("What is the first sentence?: ")
fgets(*s1[0], 75+1, stdin);
printf("What is the second sentence?:")
fgets(*s2[0],35+1,stdin);
string_cat(*s1,*s2);
printf("The two sentences added together produce the following: %c",s1 )
}
++i
return 0;
}
aside from the mistake with the for loop that others have pointed out, the while loop in your str_len function is wrong.
you should've used while(s[i] != NULL) instead of s[i] = null. one equal sign, "=", is assignment; two equal signs, "==", is comparisons; and exclamation equals, "!=", means not equal.
Secondly, you reassign your s1 and s2 to different memory locations in your string_cat function with their first character as NULL, "\0". this will always give your str_len a length of 0 if corrected your str_len function as pointed out above, and a length of random number if not corrected based on what's occupying your memory at run time.
thirdly [still in the string_cat function], your if(int x = 0; len1 + len2 < 80; \0; doesn't make sense. you're not doing any concatenations in this function at all.
Sorry for not providing you with the solution as this is a simple exercise. I feel like spoiling you if I were to provide you with the code.
First problem is here
int i=0;
for (i; i !=0; ++i)
You set value 0 to the variable i, and then you check if it does not equal 0. This check does not obviosly pass because i equals 0.
The second problem is also the loop. I can't really get the reason you need the loop it at all, because i is not used at all, exept the increment. So as far as i get it, the loop is not needed at all.
In your code having lot of compilation error. Copy paste the code what you have compiled.
Check this line of code
int i=0;
for (i; i !=0; ++i)
Because of this you are not getting any thing. In for loop you have condition i !=0 which always fail so it's not entering inside the loop.
The program is supposed to remove everything but the letters and create a new string which will have only the letters in upper-case.
However, it is not printing the results.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *remove_up(char input[])
{
char *new_str = (char *) malloc(strlen(input) + 1);
int i=0;
int j=0;
while (i < strlen(input))
{
if (((input[i]) >= 65 && (input[i]<=90)) || ((input[i]>=97) && (input[i]<=122)))
{
new_str[j]= toupper(input[i]);
i++;
j++;
}
else i++;
}
return new_str;
}
int main()
{
char str_1[100];
char str_2[100];
printf("Enter first word: ");
fgets(str_1, sizeof(str_1), stdin);
printf("Enter second word: ");
fgets(str_2, sizeof(str_2), stdin);
char *up_str_1 =(char *) malloc(strlen(str_1) + 1);
char *up_str_2 =(char *) malloc(strlen(str_2) + 1);
up_str_1= remove_up(str_1);
up_str_2= remove_up(str_2);
printf("%s", up_str_1);
printf("\n");
printf("%s", up_str_2);
return 0;
}
There are a few problems, but because this is tagged homework, I'll point them out but not give you the answer.
First of all, this doesn't do what you think:
int i, j = 0;
j will be initialized, but i probably won't start at 0. You need to initialize i to 0 as well.
Next, there's a typo - you missed a closing ] at (input[i<=122).
Finally, based on your answers to the questions, you probably aren't printing the result anyway: look up printf() or cout or whatever you prefer to use for outputting values.
It doesn't print results because you haven't used any print statements to show what comes back from your calls to remove_up.
To understand what is going on in your remove_up function, you need to understand this:
http://www.asciitable.com/
This code:
if (((input[i]) >= 65 && (input[i]<=90)) || ((input[i]>=97) && (input[i<=122)))
Is checking to see if a character is an alphabetic character in the ascii character set between these two ranges. Look at the link above. If it is in this set it's converting it to upper (redundant for half the data) and saving the result in your newly malloc'd string.
Problems:
1. You never set a null terminator in "new_str"
2. You never seem to free anything (though in this code it is trivial, in real code you could create problems, i.e. memory leaks).
3. "i" is redundant in the while loop. It's in both the if and else...
4. Rethink how you're using malloc (you probably don't want to use it this way in your custom functions unless you're going to cleanup after yourself)
There is probably more I'm missing, but that should help you see some problems.
Double check your use of parenths - you have more than needed. You are also missing a ']' in that if statement. Surprised it compiles.
change int i, j = 0; to int i = 0, j = 0;. Your i was initialized with a garbage value greater than strlen(input), and hence never entered the while loop.